人,只有自己站起来,这个世界才能属于他。|

园龄:粉丝:关注:

CF Round 1005 题解合集

here.

困难场。

C

考虑贪心,我们在一个前缀只取正数,后缀只取负数,这样取一定可以取到最大值。

扫一遍记录前缀正数和和后缀负数和,枚举分割点即可。

复杂度 O(n)

#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,a[200005],pre[200005],suf[200005],ans;
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>t;
	while(t--){
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>a[i];
		}
		for(int i=1;i<=n;i++){
			if(a[i]>=0) pre[i]=pre[i-1]+a[i];
			else pre[i]=pre[i-1];
		}
		for(int i=n;i>=1;i--){
			if(a[i]>=0) suf[i]=suf[i+1];
			else suf[i]=suf[i+1]-a[i];
		}
		for(int i=0;i<=n;i++){
			ans=max(ans,pre[i]+suf[i+1]);
		}
		cout<<ans<<'\n';
		ans=0;
		for(int i=0;i<=n+1;i++){
			pre[i]=suf[i]=0;
		}
	}
	return 0;
}

D

一个观察:x 的最高位 1 只会左移。

同时,如果 wi 的最高位 1x 的最高位 1 靠右,那么 wi 一定会被吃。

所以考虑只有在 wi 的最高位 1 大于等于 x 的最高位 1 时,再考虑 x 是否合法。

容易发现,这样的位置只有 O(logv) 个。

为了确定这样的 wi 的位置,考虑预处理跳跃数组:fi,j 表示从 i 往前跳,第一个最高位 2j 的位置。

为了得知 x 跳一个区间的异或和,考虑预处理异或前缀和数组。

然后就可以暴力跳了,注意边界的细节。

复杂度 O(nlogv)

#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,m,x,a[200005],f[200005][31],cnt[31],sum[200005],p,q;
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>t;
	while(t--){
		cin>>n>>m;
		for(int i=1;i<=n;i++){
			cin>>a[i];
		}
		for(int i=1;i<=n+1;i++){
			for(int j=0;j<=30;j++){
				f[i][j]=cnt[j];
			}
			for(int j=30;j>=0;j--){
				if(a[i]&(1<<j)){
					p=j;
					break;
				}
			}
			for(int j=0;j<=p;j++){
				cnt[j]=i;
			}
		}
		for(int i=n;i>=1;i--){
			sum[i]=sum[i+1]^a[i];
		}
		for(int i=1;i<=m;i++){
			cin>>x;
			p=n+1;
			for(int j=30;j>=0;j--){
				if(x&(1<<j)){
					q=f[p][j];
					if(!q){
						p=q+1;
						break;
					}
					if((x^sum[p]^sum[q+1])>=a[q]){
						x^=(sum[p]^sum[q]);
						p=q;
					}else{
						p=q+1;
						break;
					}
				}
			}
			cout<<n-p+1<<' ';
		}
		cout<<'\n';
		for(int i=0;i<=30;i++){
			cnt[i]=0;
		}
		for(int i=1;i<=n+1;i++){
			sum[i]=0;
			for(int j=0;j<=30;j++){
				f[i][j]=0;
			}
		}
	}
	return 0;
}

E

做了半个下午,太困难。

观察一:c=c

第一列就是 c,不会改变。

观察二:p 是颜色相同的若干位置交换得到的。

可以通过每个颜色块的位置,确定每个颜色 ci 的每一行数量集合,同一颜色的 pi 只会交换位置,不会改变值。

观察三:ci=cjpi,pj 能够交换,当且仅当 maxk=ijpkmin(pi,pj)

手玩几下样例就能看出来。

然后就差不多了。

考虑对于一个位置 pi,它能交换的位置集合是一段区间。

不难发现,对于同一种颜色,这些区间只有相互包含和不交两种可能,并且一定是 p 大的包含 p 小的。

最终的合法方案是每个点在区间内选择一个,可以从下往上用乘法原理计算。

然后就可以从小到大枚举一种颜色中的所有位置,用并查集维护这个树形结构,合并时计算答案。

实现上,还需要线段树计算每个节点的交换区间。

复杂度 O(nlogn)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
int t,n,ord[200005],p[200005],c[200005];
vector<int> v[200005],o[200005];
bool cmp(int x,int y){
	return p[x]<p[y];
}
int fa[200005],siz[200005],L[200005],R[200005],ans;
void init(){
	for(int i=1;i<=n;i++){
		fa[i]=i;
		siz[i]=1;
	}
}
int find(int x){
	if(fa[x]==x) return x;
	else return fa[x]=find(fa[x]);
}
void merge(int x,int y){
	x=find(x);
	y=find(y);
	if(x==y) return;
	fa[x]=y;
	siz[y]+=siz[x];
	L[y]=min(L[y],L[x]);
	R[y]=max(R[y],R[x]);
}
int val[400005],ls[400005],rs[400005],dcnt,rt;
void pushup(int x){
	val[x]=max(val[ls[x]],val[rs[x]]);
}
void build(int l,int r,int &x){
	x=++dcnt;
	if(l==r){
		val[x]=p[l];
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,ls[x]);
	build(mid+1,r,rs[x]);
	pushup(x);
}
void modify(int l,int r,int pos,int k,int x){
	if(l==r){
		val[x]=k;
		return;
	}
	int mid=(l+r)>>1;
	if(pos<=mid) modify(l,mid,pos,k,ls[x]);
	else modify(mid+1,r,pos,k,rs[x]);
	pushup(x);
}
int query(int l,int r,int ql,int qr,int x){
	if(ql<=l && r<=qr) return val[x];
	int mid=(l+r)>>1,ans=0;
	if(ql<=mid) ans=max(ans,query(l,mid,ql,qr,ls[x]));
	if(qr>=mid+1) ans=max(ans,query(mid+1,r,ql,qr,rs[x]));
	return ans;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>t;
	while(t--){
		ans=1;
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>p[i];
		}
		for(int i=1;i<=n;i++){
			cin>>c[i];
			L[i]=R[i]=v[c[i]].size();
			v[c[i]].push_back(i);
			o[c[i]].push_back(i);
		}
		for(int i=1;i<=n;i++){
			sort(o[i].begin(),o[i].end(),cmp);
		}
		init();
		build(1,n,rt);
		for(int i=1;i<=n;i++){
			for(int j=0;j<o[i].size();j++){
				modify(1,n,o[i][j],0,rt);
			}
			for(int j=0;j<o[i].size();j++){
				int k=lower_bound(v[i].begin(),v[i].end(),o[i][j])-v[i].begin();
				for(int l=k-1;l>=0;l--){
					if(query(1,n,v[i][l],v[i][k],rt)>=p[v[i][k]]) break;
					merge(v[i][k],v[i][l]);
					l=L[find(v[i][k])];
				}
				for(int l=k+1;l<v[i].size();l++){
					if(query(1,n,v[i][k],v[i][l],rt)>=p[v[i][k]]) break;
					merge(v[i][k],v[i][l]);
					l=R[find(v[i][k])];
				}
				ans=ans*siz[find(v[i][k])]%mod;
				siz[find(v[i][k])]--;
			}
			for(int j=0;j<o[i].size();j++){
				modify(1,n,o[i][j],p[o[i][j]],rt);
			}
		}
		cout<<ans<<'\n';
		dcnt=0;
		for(int i=1;i<=n;i++){
			o[i].clear();
			v[i].clear();
		}
		for(int i=1;i<=n;i++){
			fa[i]=0;
			L[i]=0;
			R[i]=0;
		}
	}
	return 0;
}

本文作者:Kenma

本文链接:https://www.cnblogs.com/Kenma/p/18720239

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   _Kenma  阅读(16)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起