zoukankan      html  css  js  c++  java
  • Codeforces Round #721(Div.2)题解(1527A~E)

    Codeforces Round #721(Div.2)题解(1527A~E)

    Prepared by Wogua_boy

    整完OS期中考终于把这场补完了,少有的能让我全部补完的场..

    A.And Then There Were K

    题意:

    给出一个数(n),找到最大的(k),使得(n&(n-1)&(n-2)&(n-3)&...&k=0)

    (n leq 10^9)

    题解:

    二进制运算。

    考虑(n)的二进制形式:(100...01101),随便写的一个,可以发现必须要把第一位二进制位(&)掉。

    那么就贪心的取(k)为:(011111......1),位数和(n)相同。在(&)的过程中剩下的所有二进制位也必然会被(&)掉。

    时间复杂度(O(logn))

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
    	int x=0,f=1;char c=getchar();
    	while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
    	while (c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    	return x*f;
    }
     
    const int maxn=2e5+100;
     
    int t,n,a[maxn];
    int main () {
    	t=read();
    	while (t--) {
    		n=read();
    		int cnt=0;
    		while (n) {
    			cnt++;
    			n/=2;
    		}
    		printf("%d
    ",(1<<(cnt-1))-1);
    	}
    }
    

    B1.Palindrome Game (easy)

    题意:

    给出一个01字符串,Alice和Bob在字符串上博弈,(Alice)先手。

    每个人可以做两种操作:

    (1)将一个本来为0的位改成1,花费1。

    (2)翻转整个字符串,花费0。但前提是这个串不能是回文串,以及,上一次操作不能是(2)。

    在easy版本中,保证字符串一开始是回文的。

    Alice和Bob都足够聪明,谁会取得胜利?

    题解:

    博弈+分类讨论。

    首先,每个人的贪心策略肯定是让字符串以回文的状态进入下一轮。

    考虑在0000上博弈:

    0000
    1000 Alice 1
    0001 Bob 0
    1001 Alice 1
    1101 Bob 1
    1011 Alice 0
    1111 Bob 1
    

    这样两者都尽可能的贪心,但是发现打平了。

    事实上后手可以通过这样一个trick来获得胜利:

    0000
    1000 Alice 1
    1001 Bob 1
    1101 Alice 1
    1011 Bob 0
    1111 Alice 1
    

    大致意思就是,每当对手企图破坏回文状态,就在对手操作的回文位置补一位,如果对手只差一步就构造出回文串,就翻转字符串。

    这样后手可以稳定比先手少花费2获得胜利。

    当字符串为奇数长度,同时中间位置的字符是0的时候,Alice可以通过修改回文中心位置来转换先后手从而取得胜利。

    当全为1的时候平局。

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
    	int x=0,f=1;char c=getchar();
    	while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
    	while (c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    	return x*f;
    }
     
    const int maxn=2e5+100;
     
    //单点修改
    //翻转,在不是回文同时上一次操作不是翻转的前提下
    int t,n;
    string s;
    int main () {
    	t=read();
    	while (t--) {
    		n=read();
    		cin>>s;
    		int f=0;
    		for (int i=0;i<s.size();i++) if (s[i]=='0') f++;
    		if (!f) {
    			printf("DRAW
    ");
    			continue;
    		}
    		if (n%2==1&&s[n/2]=='0'&&f!=1) {
    			printf("ALICE
    ");
    			continue;
    		} 
    		printf("BOB
    ");
    	}
    }
    
    

    B2.Palindrome game (hard)

    题意:

    在B1的基础上,给出的字符串任意。

    题解:

    博弈+分类讨论。

    考虑走到回文状态的先后手。

    首先Alice先手,一开始不是回文的情况下,Alice可以一直翻转字符串,强制Bob去修改,当Bob只差一步构造出回文串的时候,Alice补那一下,强制Bob下一次继续修改,Alice从而在回文串状态下获得后手。

    考虑奇数长度下中心位置为0的情况,可以发现对答案没有影响。

    这种特殊情况:只有两个0,一个在中心,应该输出平局。没有操作空间。

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
    	int x=0,f=1;char c=getchar();
    	while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
    	while (c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    	return x*f;
    }
     
    const int maxn=2e5+100;
     
    //单点修改
    //翻转,在不是回文同时上一次操作不是翻转的前提下
    int t,n;
    string s;
    int main () {
    	t=read();
    	while (t--) {
    		n=read();
    		cin>>s;
    		string s1=s;
    		reverse(s1.begin(),s1.end());
    		int f=0;
    		for (int i=0;i<s.size();i++) if (s[i]=='0') f++;
    		if (!f) {
    			printf("DRAW
    ");
    			continue;
    		}
    		if (s1==s) {
    			if (!f) {
    				printf("DRAW
    ");
    				continue;
    			}
    			if (n%2==1&&s[n/2]=='0'&&f!=1) {
    				printf("ALICE
    ");
    				continue;
    			} 
    			printf("BOB
    ");
    			continue;
    		}
    		f=0;
    		int cnt=0;
    		for (int i=1;i<=n/2;i++) {
    			if (s[i-1]!=s[n-i]) {
    				f++;
    			}
    			if (s[i-1]==s[n-i]&&s[i-1]=='0') {
    				cnt++;
    			}
    		}
    		if (f==1) {
    			if (n%2==1&&s[n/2]=='0') {
    				if (cnt==0)
    					printf("DRAW
    ");
    				else
    					printf("ALICE
    ");
    			}
    			else {
    				printf("ALICE
    ");
    			}
    		}
    		else
    		printf("ALICE
    ");
    	}
    }
    
    

    C.Sequence Pair Weight

    题意:

    定义一个序列的重量为((i,j)i<j)的组数使得(a_i=a_j)

    给出一个序列a,询问它所有子串的重量之和。

    题解:

    考虑一组(a_i=a_j)会影响多少个子序列,显然是(i(n-j+1))个子串。

    那么就枚举一遍,每个位置(i)对答案的贡献就是前面所有和(a_i)相等的位置的下标之和乘上(n-i+1)

    随便找个数据结构维护一下就行。

    时间复杂度(O(nlogn))

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
    	int x=0,f=1;char c=getchar();
    	while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
    	while (c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    	return x*f;
    }
     
    const int maxn=2e5+100;
    int t,n;
    int a[maxn];
    int main () {
    	t=read();
    	while (t--) {
    		n=read();
    		for (int i=1;i<=n;i++) a[i]=read();
    		map<int,long long> mp;
    		long long ans=0;
    		for (int i=1;i<=n;i++) {
    			ans+=1ll*mp[a[i]]*(n-i+1);
    			mp[a[i]]+=i;
    		}
    		printf("%lld
    ",ans);
    	}
    }
     
    

    D.MEX Tree

    题意:

    给出一棵树,下标0到n-1。

    对从0到n的每个k,求出有多少条路径MEX=k。

    题解:

    树上分类讨论+容斥

    首先容斥计算0的答案,有多少条路径不包含0?

    答案是取出0的所有相邻节点,他们内部的子树的路径数量加起来。

    然后容斥计算1的答案,有多少条路径包含0不包含1?

    所有经过0的路径减去经过01的路径,经过01的路径数量就是先确定01的位置,然后求出0相对于1的子树大小x和1相对于0的子树大小y,xy就是答案。这里x相对于y的子树或y相对于x的子树在下文统称相对子树。

    然后从2开始计算答案。

    假设当前计算的点是x。

    我们要求有多少条路径包含0到x-1,却不包含x。

    用A,B表示包含0到x-1的路径的最短路径的起点和终点。

    如果x在A或B的相对子树内,直接将A或B的相对子树大小减去i的子树大小,根据01路径的计算方法求一遍即可,然后把A或B更新为i即可。

    如果i在A到B的路径上,那么i的答案注定是0。因为要构造包含0到i-1的路径,必定会经过i。

    如果i既不在A或B的相对子树内,也不在A到B的路径上,当前的答案就是A的相对子树大小乘上B的相对子树,同时后面所有路径都无法构造。

    时间复杂度(O(nlogn))

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
    	int x=0,f=1;char c=getchar();
    	while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
    	while (c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    	return x*f;
    }
    
    const int maxn=2e5+100;
    int t,n;
    int sz[maxn]; 
    int dfn[maxn];
    int tot=0;
    vector<int> g[maxn];
    int father[30][maxn];
    int h[maxn];
    void dfs (int x,int pre) {
    	sz[x]=1;
    	dfn[x]=++tot;
    	for (int y:g[x]) {
    		if (y==pre) continue;
    		h[y]=h[x]+1;
    		father[0][y]=x;
    		dfs(y,x);
    		sz[x]+=sz[y];
    	}
    }
    int lca (int x,int y) {
    	if (h[x]<h[y]) swap(x,y);
    	for (int i=20;i>=0;i--) {
    		if (h[x]-h[y]>>i) x=father[i][x];
    	}
    	if (x==y) return x;
    	for (int i=20;i>=0;i--) {
    		if (father[i][x]!=father[i][y]) {
    			x=father[i][x];
    			y=father[i][y];
    		}
    	}
    	return father[0][x];
    }
    int main ()  {
    	t=read();
    	while (t--) {
    		n=read();
    		tot=0;
    		for (int i=0;i<=20;i++) for (int j=0;j<n;j++) father[i][j]=0;
    		for (int i=0;i<n;i++) g[i].clear(),h[i]=0;
    		for (int i=1;i<n;i++) {
    			int x=read();
    			int y=read();
    			g[x].push_back(y);
    			g[y].push_back(x);
    		}
    		dfs(0,-1);
    		for (int i=1;i<=20;i++) for (int j=0;j<n;j++) father[i][j]=father[i-1][father[i-1][j]];
    		//容斥计算0的答案
    		//就是0的所有子树内部算一下路径即可
    		long long ans=0;
    		for (int y:g[0]) {
    			ans+=1ll*sz[y]*(sz[y]-1)/2;
    		} 
    		printf("%lld ",ans);
    		//容斥计算1的答案
    		//就是所有经过0的路径减去经过01的路径
    		//1子树内的点不考虑即可 
    		ans=0;
    		long long sum=1;
    		int fa=-1;
    		for (int y:g[0]) {
    			int x=sz[y];
    			if (dfn[1]>=dfn[y]&&dfn[1]<=dfn[y]+sz[y]-1) {
    				//如果1在y的子树内
    				fa=y;
    				x-=sz[1]; 
    			}
    			ans+=1ll*x*sum;
    			sum+=x;
    		}
    		printf("%lld ",ans);
    		int A=0,B=1;//AB分别表示之前路径的起点和终点
    		//fa表示如果A为0,B在A的哪个子树 
    		for (int i=2;i<=n;i++) {
    			//计算mex=i的答案
    			//如果i==n
    			if (i==n) {
    				printf("1 ");
    				break;
    			} 
    			
    			
    			//先讨论i的位置
    			//i在B的子树内
    			if (dfn[i]>=dfn[B]&&dfn[i]<=dfn[B]+sz[B]-1) {
    				ans=0;
    				long long x,y;
    				if (A==0) {
    					x=n-sz[fa];
    				}
    				else {
    					x=sz[A];
    				}
    				y=sz[B]-sz[i];
    				ans=1ll*x*y;
    				printf("%lld ",ans);
    				B=i;
    			} 
    			else {
    				//如果i在A的子树内
    				if (A==0&&(dfn[i]<dfn[fa]||dfn[i]>dfn[fa]+sz[fa]-1)) {
    					ans=0;
    					long long x,y;
    					x=n-sz[fa]-sz[i];
    					y=sz[B];
    					ans=1ll*x*y;
    					printf("%lld ",ans);
    					A=i;
    				} 
    				else if (A!=0&&dfn[i]>=dfn[A]&&dfn[i]<=dfn[A]+sz[A]-1) {
    					ans=0;
    					long long x,y;
    					x=sz[A]-sz[i];
    					y=sz[B];
    					ans=1ll*x*y;
    					printf("%lld ",ans);
    					A=i;
    				}
    				//如果i在A到B的路径上
    				//答案是0 
    				else if (lca(A,i)==i||lca(B,i)==i) {
    					ans=0;
    					printf("%lld ",ans);
    				}
    				//如果不在 
    				//答案是包含A到B的路径的路径数量
    				// 
    				else {
    					long long x,y;
    					ans=0;
    					if (A==0) x=n-sz[fa];
    					else x=sz[A];
    					y=sz[B];
    					ans=1ll*x*y;
    					printf("%lld ",ans);
    					for (int j=i+1;j<=n;j++) printf("0 ");
    					break;
    				}
    			}
    		}
    		printf("
    ");
    	}
    } 
    
    

    E.Partition Game

    题意:

    给出一个长度为n的数组a。

    一个数组t的花费是(cost(t)=sum_{x in set(t)}last(x)-first(x))

    请你把数组a划分成k段,使得每一段的花费之和最小。

    (n leq 3e5+5e4,k leq 100)

    题解:

    这题完全看的官方题解,用线段树维护dp过程的转移。

    让我们用dp解决这个问题。

    考虑用(f(i,j))表示前(j)个元素分成(i)段的答案。

    (c(i,j))表示从下标(i)(j)的子段花费。

    那么有以下转移方程:

    (f(i,j)=min_{k=1}^{j-1}(f(i-1,k)+c(k+1,j)))

    如果暴力计算,时间复杂度(O(n^2k))

    现在我们需要用数据结构来计算(f(i-1,k)+c(k+1,j))这个过程。

    我们开一个新数组(b_k=k-lst_{a_k})(lst_{a_k})表示(a_k)的上一次出现位置。

    然后,我们用(k)颗线段树维护,比如我们已经维护了前缀(j)的答案,对前缀(j+1),我们只需要在([0,lst_{a_{j+1}}-1])这个区间上加上(b_{j+1})即可。

    时间复杂度(O(nklogn))

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
    	int x=0,f=1;char c=getchar();
    	while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
    	while (c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    	return x*f;
    }
    
    const int maxn=2e5+100;
    int n,k;
    int a[maxn];
    struct node {
    	int l,r;
    	int sum;
    	int lazy;
    }segTree[maxn*4];
    int f[maxn];
    void build (int i,int l,int r) {
    	segTree[i].l=l;
    	segTree[i].r=r;
    	segTree[i].lazy=0;
    	if (l==r) {
    		segTree[i].sum=f[l];
    		return;
    	}
    	int mid=(l+r)>>1;
    	build(i<<1,l,mid);
    	build(i<<1|1,mid+1,r);
    	segTree[i].sum=min(segTree[i<<1].sum,segTree[i<<1|1].sum);
    } 
    void spread (int i) {
    	if (segTree[i].lazy) {
    		segTree[i<<1].sum+=segTree[i].lazy;
    		segTree[i<<1|1].sum+=segTree[i].lazy;
    		segTree[i<<1].lazy+=segTree[i].lazy;
    		segTree[i<<1|1].lazy+=segTree[i].lazy;
    		segTree[i].lazy=0;
    	}
    }
    void up (int i,int l,int r,int v) {
    	if (segTree[i].l>=l&&segTree[i].r<=r) {
    		segTree[i].sum+=v;
    		segTree[i].lazy+=v;
    		return;
    	}
    	spread(i);
    	int mid=(segTree[i].l+segTree[i].r)>>1;
    	if (l<=mid) up(i<<1,l,r,v);
    	if (r>mid) up(i<<1|1,l,r,v);
    	segTree[i].sum=min(segTree[i<<1].sum,segTree[i<<1|1].sum);
    }
    int query (int i,int l,int r) {
    	if (segTree[i].l>=l&&segTree[i].r<=r) {
    		return segTree[i].sum;
    	}
    	spread(i);
    	int mid=(segTree[i].l+segTree[i].r)>>1;
    	int ans=1e9;
    	if (l<=mid) ans=min(ans,query(i<<1,l,r));
    	if (r>mid) ans=min(ans,query(i<<1|1,l,r));
    	return ans;
    }
    int lst[maxn];
    int pre[maxn];
    int b[maxn];
    int main () {
    	n=read();k=read();
    	for (int i=1;i<=n;i++) {
    		a[i]=read();
    		lst[i]=pre[a[i]];
    		pre[a[i]]=i;
    		b[i]=i-lst[i];
    	}
    	for (int i=0;i<=n;i++) f[i]=1e9;
    	f[0]=0;
    	build(1,0,n);
    	for (int i=1;i<=k;i++)  {
    		for (int j=0;j<=n;j++) f[j]=1e9;
    		f[0]=0;
    		for (int j=1;j<=n;j++) {
    			if (lst[j]) up(1,0,lst[j]-1,b[j]);
    			f[j]=query(1,0,j-1);
    		} 
    		build(1,0,n);
    	}
    	printf("%d
    ",f[n]);
    }
    
  • 相关阅读:
    kafka报错:Invalid message size: 0
    转载:elastic5.x部署常见问题总结
    hadoop集群zookeeper迁移
    生产环境轻量级dns服务器dnsmasq搭建文档
    (3)安装elastic6.1.3及插件kibana,x-pack,essql,head,bigdesk,cerebro,ik
    (2)安装elastic6.1.3及插件kibana,x-pack,essql,head,bigdesk,cerebro,ik
    (1)安装elastic6.1.3及插件kibana,x-pack,essql,head,bigdesk,cerebro,ik
    换种思路解决日志占用磁盘空间问题
    更改hadoop集群yarn的webui中的开始时间和结束时间为本地时间
    两种虚拟机扩容方式扩容后在线生效的方法
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/14799809.html
Copyright © 2011-2022 走看看