其他分享
首页 > 其他分享> > 20220711模拟赛

20220711模拟赛

作者:互联网

F:

题意:给定n个不大于c的正整数a1~an和m组询问,每次问 [l,r] 中有多少个数出现正偶数次。
注:强制在线

一道分块题,思路类似区间分块9(众数)那道题,连续块的答案可以用暴力来维护,每次询问时散点枚举,其对答案的贡献显然。

点击查看代码
#include<bits/stdc++.h>
#define get(i) (((i-1)/len)+1)
#define get_l(i) ((i-1)*len+1)
#define get_r(i) (min(n,len*i))
#define M 100005
#define N 350
using namespace std;
int n,c,m,a[M],s[N][M],f[N][N],t[M],len;
int main(){
	scanf("%d%d%d",&n,&c,&m);len=sqrt(n);
	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
	for(int i=1,tmp;i<=get(n);++i){
		tmp=0;
		for(int j=1;j<=c;++j) s[i][j]=s[i-1][j],t[j]=0;
		for(int j=i;j<=get(n);++j){
			for(int k=get_l(j);k<=get_r(j);++k){
				if(i==j) ++s[i][a[k]];
				if(t[a[k]]&1) ++tmp;
				else if(t[a[k]]) --tmp;
				++t[a[k]];
			}
		f[i][j]=tmp;
		}
	}
	int l,r,ans,lt;
	while(m--){
		scanf("%d%d",&l,&r);
		l+=lt,l%=n,l++;r+=lt,r%=n,r++;
		if(l>r) swap(l,r);
		if(get(l)==get(r)){
			ans=0;
			for(int i=l;i<=r;++i) t[a[i]]=0;
			for(int i=l;i<=r;++i){
				if(t[a[i]]&1) ++ans;
				else if(t[a[i]]) --ans;
				++t[a[i]];
			}
			printf("%d\n",lt=ans);
			continue;
		}
		ans=f[get(l)+1][get(r)-1];
		for(int i=l;i<=get_r(get(l));++i) t[a[i]]=s[get(r)-1][a[i]]-s[get(l)][a[i]];
		for(int i=get_l(get(r));i<=r;++i) t[a[i]]=s[get(r)-1][a[i]]-s[get(l)][a[i]];
		for(int i=l;i<=get_r(get(l));++i){
			if(t[a[i]]&1) ++ans;
			else if(t[a[i]]) --ans;
			++t[a[i]];
		}	
		for(int i=get_l(get(r));i<=r;++i){
			if(t[a[i]]&1) ++ans;
			else if(t[a[i]]) --ans;
			++t[a[i]];
		}	
		printf("%d\n",lt=ans);
	}
	return 0;
}

H:

题意:给出一个大小为n的有根树,每个点有一个值a[i]。
定义一条路径的靠谱度为路径上所有ai的和。
定义路径的长度为路径上点的数量。
定义路径上的点为b1,b2,b3......(b[i]和b[i+1]有一条边),则b[i]为b[i+1]的父亲。
要求找出m条互不相同的路径,每条路径的长度为[L,R],使这些路径的靠谱值最大。
求最大的靠谱值之和。

首先我们先尝试一下本题的简化版:超级钢琴
对于这道题目可以把一个枚举左端点,而右端点是一段区间,区间最值易得。
所以我们可以把一个三元组放到堆里,其中表示左端点以及这个区间,
排列顺序为后面的区间最值与左端点权值之差,每次取出来之后再把这个区间从最值点劈开,把剩下两个区间与该节点的组成的三元组继续放到堆里。

点击查看代码
#include<bits/stdc++.h>
#define M 500005
using namespace std;
int n,k,L,R;
long long sm[M];
struct node{
	long long  bei_zeng[M][35];
	void init(){
		for(int i=1;i<=n;++i) bei_zeng[i][0]=i;
		for(int j=1;(1<<j)<=n;++j)
			for(int i=1;i+(1<<j)-1<=n;++i){
				int x=bei_zeng[i][j-1],y=bei_zeng[i+(1<<(j-1))][j-1];
				bei_zeng[i][j]=sm[x]>sm[y]?x:y;
			}
	} 
	int query(int l,int r){
		int k=log2(r-l+1);
		int x=bei_zeng[l][k],y=bei_zeng[r-(1<<k)+1][k];
		return sm[x]>sm[y]?x:y;
	}
}tr;
struct E{
	int o,l,r;
	
};
bool operator < (E a,E b){
	return sm[tr.query(a.l,a.r)]-sm[a.o-1]<sm[tr.query(b.l,b.r)]-sm[b.o-1];
}
priority_queue<E> q;
int main(){
	scanf("%d%d%d%d",&n,&k,&L,&R);
	for(int i=1;i<=n;++i){
		scanf("%lld",&sm[i]);
		sm[i]+=sm[i-1];
	}
	tr.init();
	for(int i=1;i<=n;++i)
		if(i+L-1<=n)
			q.push(E{i,i+L-1,min(i+R-1,n)});
	long long ans=0;
	while(k--){
		int o=q.top().o,l=q.top().l,r=q.top().r,t;
		t=tr.query(l,r);
		q.pop();
		ans+=sm[t]-sm[o-1];
		if(l!=t) q.push(E{o,l,t-1});
		if(r!=t) q.push(E{o,t+1,r});
	}	
	printf("%lld",ans);
	return 0;
}
那么他到了树上呢?

其实是一个道理。用一个点向上倍增,找到那个合法的[L,R]区间,之后用前缀和数组差分维护区间之和,依旧用RMQ处理三元组,并且每一次找最大,分裂后再放回。

点击查看代码
#include<bits/stdc++.h>
#define M 500005
using namespace std;
int n,f[M][20],dep[M],sm[M][20],m,L,R,lg[M];
long long a[M],ans;
int find(int p,int k){
    for(;k;k-=(k&(-k))) p=f[p][__builtin_ctz(k)];
    return p;
}
void init(){
    for(int i=1;i<=n;++i) 
        for(int j=1;(1<<j)<=n;++j)
            f[i][j]=f[f[i][j-1]][j-1],
            sm[i][j]=a[sm[i][j-1]]<a[sm[f[i][j-1]][j-1]]?sm[i][j-1]:sm[f[i][j-1]][j-1];
}
int query(int l,int r){
    int k=lg[dep[l]-dep[r]+1];
    int x=sm[l][k],y=sm[find(l,dep[l]-dep[r]+1-(1<<k))][k];
    return a[x]<a[y]?x:y;
}
struct node{
    int o,l,r,t;
};
bool operator < (node qwe,node ewq){
    return a[qwe.t]-a[qwe.o]>a[ewq.t]-a[ewq.o];
}
priority_queue<node> q;
int main(){
    scanf("%d",&n);
    for(int i=2;i<=n;++i) lg[i]=lg[i>>1]+1;
    for(int i=1;i<=n;++i){scanf("%d",&f[i][0]);dep[i]=dep[f[i][0]]+1;sm[i][0]=i;}
    for(int i=1;i<=n;++i){scanf("%lld",&a[i]);a[i]+=a[f[i][0]];}
    scanf("%d%d%d",&m,&L,&R);
    init();
    for(int i=1;i<=n;++i)
        if(dep[i]>=L){ int l=find(i,L);int r=find(i,R);/*printf("%d %d %d %d\n",i,l,r,query(l,r));*/q.push(node{i,l,r,query(l,r)});}
    while(m--){
        int o=q.top().o,l=q.top().l,r=q.top().r,t=q.top().t;
        q.pop();
        ans+=a[o]-a[t];
        int nw_l=l,nw_r=find(o,dep[o]-dep[t]-1);
        if(t!=l) q.push(node{o,nw_l,nw_r,query(nw_l,nw_r)});
        nw_l=f[t][0];nw_r=r;
        if(t!=r) q.push(node{o,nw_l,nw_r,query(nw_l,nw_r)});
    }
    printf("%lld",ans);
    return 0;
}

其他题目:

A:

求极差
题解略

B:

二进制数*17后输出
从下往上进位

C:

题意:
一共有N块木板,每块长度为hi,宽度为1.
乐乐要用其中的若干块来造篱笆.

每一块篱笆,必须由两块木板连起来,宽度1,长度为两块之和,连接后插到地上.
所有篱笆必须一样高.
他想知道,最多能造几块篱笆? 在这个块数下,有几种不同高度的方案?

只需要枚举答案,然后循环得到答案。

D:

题意:
FJ的n头奶牛现在都在栅栏旁,它们排成一条直线,每头牛的位置都不同。牛的颜色有两种,一种是白色的,一种是斑点的,且至少有一头白色的牛。
FJ现在想对一些连续的牛进行拍照,他希望这个区间的白色牛和斑点牛一样多。如果这样的区间有多个,他希望最左边牛的位置和最右边牛的位置之差最大(照片可以拍到的距离最长)。
FJ发现拍照的时候,让两种牛的个数相同,是在太难了。他想通过其他的办法来实现,photoshop是个不错的选择,他可以在拍照之后,把白色牛变成斑点牛。
请你帮他算算,他可以拍到的最长距离是多少?

这样的区间满足白牛大于等于斑牛,并且二者之差为偶数;所以我们可以按前缀和的奇偶开两个树状数组,枚举右端点,寻找最小左端点。

E

一道分块题,具体怎么维护易得。

G

倍增+并查集
每一个合并区间拆成log个,然后按照区间大小依次转移

标签:20220711,int,long,区间,query,nw,模拟,define
来源: https://www.cnblogs.com/goldenwalnut/p/16469405.html