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

园龄:粉丝:关注:

CF Round 1001 题解合集

here.

草怎么是贪心专场。

问就是不会 F。

C

注意到操作一至多只会进行一次,进行两次就抵消了。

所以直接枚举所有可能操作取最大值即可。

注意要开 long long。

总体复杂度 O(tn3)

#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,ans,a[51],b[51],c[51];
void get(int cnt){
	memcpy(c,b,sizeof(c));
	for(int i=1;i<=n-cnt;i++){
		b[i]=c[i+1]-c[i];
	}
}
int solve(int now){
	memcpy(b,a,sizeof(b));
	int sum=0,ans=0;
	for(int i=1;i<=n;i++){
		ans+=b[i];
	}
	int cnt=1;
	while(cnt<n){
	    if(now==cnt) reverse(b+1,b+1+n-cnt+1);
		get(cnt); 
		sum=0;
		for(int i=1;i<=n-cnt;i++){
			sum+=b[i];
		}
		ans=max(ans,sum);
		cnt++;
	}
	return 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];
			ans+=a[i];
		}
		for(int i=0;i<n;i++){
			ans=max(ans,solve(i));
		}
		cout<<ans<<'\n';
		ans=0;
	}
	return 0;
}

D

草感觉比 E 困难啊。

为了方便,我们强制每次操作的 (u,v) 相连。

首先钦定 1 为根,假设 a 是已知的,不难发现对于每次操作 (u,v),它们是相互独立的,只影响 (u,v) 两点的相对点权。

想要最小化点权,那么对于每个点,应该这样操作:

  • 如果 ax<afa,那么令 u=fa,v=x,使得 axafa

  • 如果 ax>afa,那么令 u=x,v=fa,使得 afax

不难发现,这样操作后,点权为 xmax(0,axafa)。此处认为 1 的父亲是 0

所以,我们的问题转化为最小化 xmax(0,axafa)

不难发现,在没有区间限制的前提下,令 axmaxysonx(ay) 是最优的。

加上区间限制之后,结论依然成立。

注意对于叶子节点来说,令 axlx 是最优的。

按题意模拟贪心策略,总体复杂度 O(n)

#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,maxn,l[200005],r[200005],x,y;
int head[200005],nxt[400005],target[400005],tot;
void add(int x,int y){
	tot++;
	nxt[tot]=head[x];
	head[x]=tot;
	target[tot]=y;
}
int f[200005],a[200005];
void dfs(int x,int fa){
	f[x]=fa;
	bool flag=true;
	for(int i=head[x];i;i=nxt[i]){
		int y=target[i];
		if(y==fa) continue;
		dfs(y,x);
		a[x]=max(a[x],max(l[x],a[y]));
		flag=false;
	}
	if(flag) a[x]=l[x];
	a[x]=min(a[x],r[x]);
}
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>>l[i]>>r[i];
		}
		for(int i=1;i<n;i++){
			cin>>x>>y;
			add(x,y);
			add(y,x);
		}
		dfs(1,0);
		for(int i=1;i<=n;i++){
			maxn+=max(0ll,a[i]-a[f[i]]);
		}
		cout<<maxn<<'\n';
		for(int i=1;i<=n;i++){
			head[i]=0;
			a[i]=0;
			f[i]=0;
		}
		tot=maxn=0;
	}
}

E1

经过手玩样例发现,对于 x,如果存在 y 不在 x 子树内且 wy>wx,此时 wx 最大的 x 一定是必胜点。

原因是,这样的 x 满足,无论后手操作任意 y,先手都无法再次操作。

考虑用反证法证明。

假设操作完 x 后,依然存在两点 y,z,使得后手操作 y 后,先手依然可以操作 z,不难发现 wy<wz

考虑分类讨论:

  • 如果 y,z 存在子孙关系,那么只能是 yz 的祖先,否则 x 不是 wx 最大的点,y 才是。而如果 yz 的祖先,那么操作完 y 后无法再操作 z,矛盾;

  • 如果 y,z 不存在子孙关系,那么 y 一定可以取代 x 成为点权最大的点,矛盾。

所以,我们的结论是正确的。

实现上,可以用树状数组记录不同点权点的出现次数,在 dfs 的过程中差分即可。

总体复杂度 O(nlogn)

#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,x,y,ans,a[400005],b[400005],c[400005],dfn[400005];
int head[400005],nxt[800005],target[800005],tot;
void add(int x,int y){
	tot++;
	nxt[tot]=head[x];
	head[x]=tot;
	target[tot]=y;
}
bool cmp(int x,int y){
	return a[x]>a[y];
}
#define lowbit(i) (i&(-i))
void modify(int x,int k){
	for(int i=x;i<=n;i+=lowbit(i)){
		c[i]+=k;
	}
}
int query(int x){
	int ans=0;
	for(int i=x;i;i-=lowbit(i)){
		ans+=c[i];
	}
	return ans;
}
void dfs(int x,int fa){
	int now=query(n)-query(a[x]);
	modify(a[x],1);
	for(int i=head[x];i;i=nxt[i]){
		int y=target[i];
		if(y==fa) continue;
		dfs(y,x); 
	}
	if(b[x]==query(n)-query(a[x])-now) b[x]=0;
	else b[x]=1;
}
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];
			modify(a[i],1);
			dfn[i]=i;
		}
		for(int i=1;i<=n;i++){
			b[i]=query(n)-query(a[i]);
		}
		for(int i=1;i<n;i++){
			cin>>x>>y;
			add(x,y);
			add(y,x);
		}
		dfs(1,0);
		sort(dfn+1,dfn+1+n,cmp);
		for(int i=1;i<=n;i++){
			if(b[dfn[i]]){
				ans=dfn[i];
				break;
			}
		}
		cout<<ans<<'\n';
		ans=tot=0;
		for(int i=1;i<=n;i++){
			head[i]=b[i]=c[i]=0;
		}
	}
	return 0;
}

E2

草感觉是一个题,会 E1 就会 E2。

根据 E1 的结论,当前局面是必败局面当且仅当存在一个点 x,使得 x 子树外的点 y 满足 wx<wy

所以,对于先手来说,他只能一步取胜,也就是一步将局面变成必败局面。

然后,考虑哪些 x 满足在操作完它之后,当前局面是必败局面。

不难发现,这样的 x,对于任意 wy>wx,应当满足:

  1. xy 的祖先;

  2. z 表示满足点权 >wy 且不在 y 子树内的点的共同 lca,xz 的祖先。

两条满足任意一个即可。也很好理解,要么 y 不存在,要么让后手选 y 之后必输。

然后按点权从大到小加入点,用 dfs 序线段树维护每个 y 对应的 z,再用一个树状数组做树上差分即可。

总体复杂度 O(nlog2n),常数很小,可以通过。

#include<bits/stdc++.h>
using namespace std;
int t,n,m,x,y,a[400005],num,tmp1,tmp2;
vector<int> ans,v[400005];
int head[400005],nxt[800005],target[800005],tot;
void add(int x,int y){
	tot++;
	nxt[tot]=head[x];
	head[x]=tot;
	target[tot]=y;
}
int siz[400005],dfn[400005],rnk[400005],hson[400005],top[400005],dep[400005],f[400005],cnt;
void dfs1(int x,int fa){
	siz[x]=1;
	for(int i=head[x];i;i=nxt[i]){
		int y=target[i];
		if(y==fa) continue;
		dep[y]=dep[x]+1;
		f[y]=x;
		dfs1(y,x);
		siz[x]+=siz[y];
		if(siz[hson[x]]<siz[y]) hson[x]=y;
	}
}
void dfs2(int x,int t){
	cnt++;
	dfn[x]=cnt;
	rnk[cnt]=x;
	top[x]=t;
	if(!hson[x]) return;
	dfs2(hson[x],t);
	for(int i=head[x];i;i=nxt[i]){
		int y=target[i];
		if(y==f[x] || y==hson[x]) continue;
		dfs2(y,y);
	}
}
int lca(int x,int y){
	if(!x) return y;
	if(!y) return x;
	while(top[x]^top[y]){
		if(dep[top[x]]>dep[top[y]]) x=f[top[x]];
		else y=f[top[y]];
	}
	if(dfn[x]<dfn[y]) return x;
	else return y;
}
int val[800005],ls[800005],rs[800005],dcnt,rt;
void build(int l,int r,int &x){
	x=++dcnt;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(l,mid,ls[x]);
	build(mid+1,r,rs[x]);
}
void modify(int l,int r,int pos,int x){
	if(l==r){
		val[x]=rnk[l];
		return;
	} 
	int mid=(l+r)>>1;
	if(pos<=mid) modify(l,mid,pos,ls[x]);
	else modify(mid+1,r,pos,rs[x]);
	val[x]=lca(val[ls[x]],val[rs[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=lca(ans,query(l,mid,ql,qr,ls[x]));
	if(qr>=mid+1) ans=lca(ans,query(mid+1,r,ql,qr,rs[x]));
	return ans;
}
int c[400005];
#define lowbit(i) (i&(-i))
void chg(int x,int k){
	if(!x) return;
	for(int i=x;i<=n;i+=lowbit(i)){
		c[i]+=k; 
	}
}
int get(int x){
	int ans=0;
	for(int i=x;i;i-=lowbit(i)){
		ans+=c[i];
	}
	return ans;
}
void init(){
	for(int i=1;i<=n;i++){
		v[i].clear();
		head[i]=dfn[i]=rnk[i]=siz[i]=hson[i]=top[i]=c[i]=0;
	}
	for(int i=1;i<=dcnt;i++){
		val[i]=ls[i]=rs[i]=0;
	}
	ans.clear();
	cnt=tot=num=dcnt=rt=0;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>t;
	while(t--){
		cin>>n;
		init();
		for(int i=1;i<=n;i++){
			cin>>a[i];
			v[a[i]].push_back(i);
		}
		for(int i=1;i<n;i++){
			cin>>x>>y;
			add(x,y);
			add(y,x);
		}
		dfs1(1,0);
		dfs2(1,1);
		build(1,n,rt);
		for(int i=n;i>=1;i--){
			for(int j=0;j<v[i].size();j++){
				x=v[i][j];
				if(dfn[x]==1) tmp1=0;
				else tmp1=query(1,n,1,dfn[x],rt);
				if(dfn[x]+siz[x]-1==n) tmp2=0;
				else tmp2=query(1,n,dfn[x]+siz[x],n,rt);
				y=lca(tmp1,tmp2);
				if(!y) continue;
				//cout<<"qq"<<x<<' '<<get(dfn[x]+siz[x]-1)<<' '<<get(dfn[x]-1)<<'\n';
				if(get(dfn[x]+siz[x]-1)-get(dfn[x]-1)==num) ans.push_back(x);
			}
			for(int j=0;j<v[i].size();j++){
				x=v[i][j];
				if(dfn[x]==1) tmp1=0;
				else tmp1=query(1,n,1,dfn[x],rt);
				if(dfn[x]+siz[x]-1==n) tmp2=0;
				else tmp2=query(1,n,dfn[x]+siz[x],n,rt);
				y=lca(tmp1,tmp2);
				//cout<<"qwq"<<x<<' '<<y<<'\n';
				if(y){
					num++;
					if(x==y){
						chg(dfn[x],1);
					}else{
						chg(dfn[x],1);
					    chg(dfn[y],1);
					    chg(dfn[lca(x,y)],-1);
					} 
				}
			}
			for(int j=0;j<v[i].size();j++){
				x=v[i][j];
				modify(1,n,dfn[x],rt);
			}
		}
		sort(ans.begin(),ans.end());
		cout<<ans.size()<<' ';
		for(int i=0;i<ans.size();i++){
			cout<<ans[i]<<' ';
		}
		cout<<'\n';
	}
	return 0;
}

本文作者:Kenma

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

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

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