zoukankan      html  css  js  c++  java
  • 题解-比赛CF1332

    题解-比赛CF1332

    比赛CF1332

    [A] [B] [C] [D] [E] [F] [G]


    [A]Exercising Walk

    Exercising Walk

    (T) 组测试数据,每次给定 (a,b,c,d,x,y,x_1,y_1,x_2,y_2)。起点是 ((x,y)),要左走 (a) 步,右走 (b) 步,下走 (c) 步,上走 (d) 步(这题的 (x)(y) 轴与平时相反)。求是否有走法,使得走的过程中总是满足 (x_1le xle x_2)(y_1le yle y_2)。如果满足输出 ( exttt{YES}),否则输出 ( exttt{NO})

    数据范围:(1le Tle 10^3)(0le a,b,c,dle 10^8)(a+b+c+dge 1)(-10^8le x_1le xle x_2le 10^8)(-10^8le y_1le yle y_2le 10^8)

    本来以为直接找到最终点看看在不在范围内就够了,但是后来发现过不了样例。

    有一种特殊情况:([x_1=x]&[x=x_2]&[a,b>0]&[a+b=0])(一走就走出范围了),答案是 ( exttt{NO}),你会输出 ( exttt{YES})

    对于 (y,c,d) 同理,所以特判一下就过了。

    易错点:

    1. 没看清这题的 (x)(y) 轴与平时相反这个特点。
    2. 没考虑到特殊情况(你样例都过不了啊)。

    代码:

    //Data
    int a,b,c,d,x,y,x1,y1,x2,y2;
    il int yes(){
    	if(a&&b) if(x==x1&&x==x2) return 0;
    	if(c&&d) if(y==y1&&y==y2) return 0;
    	x-=a,x+=b,y-=c,y+=d;
    	return x1<=x&&x<=x2&&y1<=y&&y<=y2;
    }
    
    //Main
    int main(){
    	re int t;
    	scanf("%d",&t);
    	while(t--){
    		scanf("%d%d%d%d%d%d%d%d%d%d",&a,&b,&c,&d,&x,&y,&x1,&y1,&x2,&y2);
    		if(yes()) puts("YES"); else puts("NO");
    	}
    	return 0;
    }
    

    [B]Composite Coloring

    Composite Coloring

    (T) 组测试数据,每次给定一个长度为 (n) 的合数序列 (a_i),需要将每个数染上颜色,使满足对于任意两个颜色相同的数不互质。求一种颜色数为 (m) 的染色方案((m) 自选,不需最小,只需满足 (min[1,11]))。

    数据范围:(1le Tle 10^3)(1le nle 10^3)(4le a_ile 10^3)(1le sum nle 10^4)

    对于任意满足数据范围限制的序列绝对有解。

    对于任何合数 (a_ile 1000),必然有 (d) 满足 ([d|i]&[d<32])

    ([1,32)) 中正好有 (11) 个质数,所以遍历每个质数然后把它们的倍数染同色即可。

    易错点:

    题目中有说对于 (1sim m) 中的每个颜色,必须有该颜色的数,所以需要对颜色离散化。

    代码:

    //Data
    const int N=1000;
    int n,a[N+7],co[N+7];
    
    //Prime
    bitset<37> np;
    vector<int> p;
    
    //Main
    int main(){
    	for(re int i=2;i<=32;i++){
    		if(!np[i]) p.pb(i);
    		for(re int j:p)if(i*j>N) break;else np[i*j]=1;
    	}
    	re int t;
    	scanf("%d",&t);
    	while(t--){
    		scanf("%d",&n);
    		for(re int i=1;i<=n;i++)
    			scanf("%d",a+i);
    		re int C=0;
    		fill(co+1,co+n+1,0);
    		for(re int j:p){
    			re int ttt=0;
    			for(re int i=1;i<=n;i++){
    				if(!co[i]&&a[i]%j==0){ttt=1,co[i]=C+1;}
    			}
    			if(ttt) C++;
    		}
    		printf("%d
    ",C);
    		for(re int i=1;i<=n;i++)
    			printf("%d%c",co[i],"
     "[i<n]);
    	}
    	return 0;
    }
    

    [C]K-Complete Word

    K-Complete Word

    (T) 组测试数据,给定 (n)(k) 满足 (k|n),给定一个长度为 (n) 的字符串 (s),求最少修改 (s) 的几个字母,使得 (s) 是回文串并且对于所有 (1le ile n-k),满足 (s_i=s_{i+k})

    数据范围:(1le tle 10^5)(1le k<nle 2 imes 10^5)(sum nle 2 imes 10^5)

    第二个条件等价于 (s)(frac nk)(k) 长子段相等;因为 (s) 是回文的,所以每个 (k) 长子段也是回文的。

    所以对于每个 (1le ile lfloorfrac k2 floor),满足:

    [s_i=s_{k+1-i}=s_{k+i}=s_{k+k+1-i}=cdots=s_{(frac nk -1)cdot k+i}=s_{(frac nk -1)cdot k+k+1-i} ]

    如果 (kin mathbb{odd}),对于 (i=frac {k+1}2) 满足:

    [s_i=s_{k+i}=cdots=s_{(frac nk -1)cdot k+i} ]

    所以把每群相等的字符中出现次数最多的字符留着,把别的字符改成该字符即可。

    代码:

    //Data
    const int N=200000;
    int n,k;
    char s[N+7];
    int cnt[30];
    
    //Main
    int main(){
    	re int t;
    	scanf("%d",&t);
    	while(t--){
    		scanf("%d%d
    %s",&n,&k,s+1);
    		re int ans=0;
    		for(re int i=1;i<=k/2;i++){
    			memset(cnt,0,sizeof cnt);
    			re int tmp=0;
    			for(re int j=i;j<=n;j+=k) cnt[s[j]-'a']++,tmp++;
    			for(re int j=k+1-i;j<=n;j+=k) cnt[s[j]-'a']++,tmp++;
    			re int mx=0;
    			for(re int i=1;i<26;i++) if(cnt[i]>cnt[mx]) mx=i;
    			ans+=tmp-cnt[mx];
    		}
    		if(k&1){ //k∈odd
    			int i=(k+1)/2;
    			memset(cnt,0,sizeof cnt);
    			re int tmp=0;
    			for(re int j=i;j<=n;j+=k) cnt[s[j]-'a']++,tmp++;
    			re int mx=0;
    			for(re int i=1;i<26;i++) if(cnt[i]>cnt[mx]) mx=i;
    			ans+=tmp-cnt[mx];
    		}
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

    [D]Walk on Matrix

    Walk on Matrix

    给定一个 (k),要求构造一个随意大小为 (n imes m) 的矩阵 (a_{i,j}) 使得用

    得到的答案与从 ((1,1))((n,m)) 的最小按位与路径答案相差 (k)

    数据范围:(1le n,mle 500)(0le a_{i,j}le 3cdot 10^5)(0le kle 10^5)

    很巧妙的一题,从数据范围以及样例中可以猜测到这题有通解。

    首先这个答案 (S) 肯定是比真实答案 (Ans) 大的,所以 (S-Ans=k)

    通过研究第二个样例,可以发现,鲍勃的代码会盲目找最大的,不会考虑如 (7&3>8&3) 的情况。

    所以可以找一个 (t=2^c>k),构造如下 (3 imes 4) 矩阵:

    [egin{bmatrix} t|k&k&k&0\ t&0&k&0\ t&t&t|k&k\ end{bmatrix} ]

    所以鲍勃的代码 (dp_{3,3}=t,dp_{3,4}=0)

    而真正的最小按位与路径为 ((t|k)&k&k&k&(t|k)&k),答案为 (k)

    正好相差 (k)

    代码:

    //Data
    
    //Main
    int main(){
    	re int k,t=1;
    	scanf("%d",&k);
    	while(t<=k) t<<=1;
    	printf("3 4
    ");
    	printf("%d %d %d %d
    ",t|k,k,k,0);
    	printf("%d %d %d %d
    ",t,0,k,0);
    	printf("%d %d %d %d
    ",t,t,t|k,k);
    	return 0;
    }
    

    [E]Height All the Same

    Height All the Same

    给定 (n,m,L,R),求大小为 (n imes m) 的矩阵 (a_{i,j}) 中满足 (Lle a_{i,j}le R) 并且可以通过相邻元素一起 (+1)、元素 (+2) 两种操作使整个矩阵相等的数量 (mod 998244353)

    数据范围:(1le n,m,L,Rle 10^9)(Lle R)

    很明显 (L)(R) 的绝对大小不重要,所以设 (H=R-L+1)

    如果没有后一个限制,答案应该是 (H^{nm})

    手玩几下会发现,只要奇偶性满足要求,该矩阵就满足要求,所以:

    1. 如果 (nminmathbb{odd}):答案就为 (H^{nm})
    2. 如果 (nminmathbb{even}):答案就为 (lceilfrac{H^{nm}}{2} ceil)

    易错点:

    1. (mod 998244353),除法用逆元,幂 (mod 998244352)(欧拉定理)。
    2. 快速幂的时候,因为可能 (Hmod 998244353=0),如果 (nmmod 998244352=0),普通人写的快速幂很容易得到 (1),其实应该得到 (0)(因此我 (FST) 了)。

    代码:

    //Data
    const int Mo=998244353;
    int n,m,a,b,h;
    lng sz;
    il int Pow(re int a,re int x){
    	if(a==0) return 0;
    	re int res=1;
    	for(;x;a=1ll*a*a%Mo,x>>=1)if(x&1)res=1ll*res*a%Mo;
    	return res;
    }
    
    //Main
    int main(){
    	scanf("%d%d%d%d",&n,&m,&a,&b);
    	h=b-a+1,sz=1ll*n*m;
    	if(sz&1) printf("%lld
    ",1ll*Pow(h,sz%(Mo-1)));
    	else if(h&1) printf("%lld
    ",1ll*(Pow(h%Mo,sz%(Mo-1))+1+Mo)%Mo*Pow(2,Mo-2)%Mo);
    	else printf("%lld
    ",1ll*Pow(h%Mo,sz%(Mo-1))*Pow(2,Mo-2)%Mo);
    	return 0;
    }
    

    [F]Independent Set

    Independent Set

    给定树 (G=(V,E))(n) 个点。令 (E'in E),求所有 (E') 所有边两端的节点的独立集数量之和 (mod 998244353)(独立集大小可以为 (0)(E' eqvarnothing))。

    数据范围:(2le nle 3 imes10^5)

    这么简单的树形 ( exttt{dp}) 我竟然不会,而且想了好久,我太蒻了。

    (f_{o,i}) 表示:

    1. 如果 (o=0)(i)选或不选都可以,但是连接 (i) 号点与父亲节点的边不存在(i) 的子树中的答案。
    2. 如果 (o=1)(i)不选,连接 (i) 号点与父亲节点的边存在(i) 的子树中的答案。
    3. 如果 (o=2)(i),连接 (i) 号点与父亲节点的边存在(i) 的子树中的答案。

    以上“答案”均包括选的边为空的情况。

    如果该节点,子节点不与该节点连边或者不选是允许的:

    [f_{2,i}=prod_{toin i's~sons}(f_{0,to}+f_{1,to}) ]

    如果该节点不选,子节点怎么样都是允许的:

    [f_{1,i}=prod_{toin i's~sons}(f_{0,to}+f_{1,to}+f_{2,to}) ]

    如果该节点不与父亲节点连边,包括选或不选的情况,但是要排除所有子节点都不与该节点连边的情况(要不然节点孤立,就不是边两端的点了):

    [f_{0,i}=f_{1,i}+f_{2,i}-prod_{toin i's~sons}f_{0,to} ]

    因为答案不应包括选的边为空的情况,而且根节点不与父亲节点连边(根节点的父亲节点不存在),所以最终答案为 (f_{0,1}-1)

    代码:

    //Data
    const int N=300000,m=998244353;
    int n;
    vector<lng> f[3];
    vector<vector<int> > e;
    
    //Dfs
    il void Dfs(re int x,re int fa){
    	f[0][x]=f[1][x]=f[2][x]=1;
    	for(re int to:e[x])if(to!=fa){
    		Dfs(to,x);
    		(f[0][x]*=f[0][to])%=m;
    		(f[1][x]*=(f[0][to]+f[1][to]+f[2][to]))%=m;
    		(f[2][x]*=(f[0][to]+f[1][to]))%=m;
    	}
    	f[0][x]=(f[1][x]+f[2][x]-f[0][x]+m)%m;
    }
    
    //Main
    int main(){
    	scanf("%d",&n);
    	e.resize(n+7);
    	for(re int i=0;i<3;i++) f[i].resize(n+7);
    	for(re int i=1,u,v;i<n;i++)
    		scanf("%d%d",&u,&v),e[u].pb(v),e[v].pb(u);
    	Dfs(1,0);
    	printf("%lld
    ",(f[0][1]+m-1)%m);
    	return 0;
    }
    

    未完待续


    祝大家学习愉快!

  • 相关阅读:
    安装Ubuntu后需要做的事
    LaTeX的安装并使其能够编译中文
    让Windows中的文件名支持大小写
    安装VMware Tools的步骤
    强制删除文件或文件夹的方法
    工作中遇到的一些小问题
    redis主从复制
    Redis事务
    redis配置文件基本解析以及RDB持久化与AOF持久化
    redis 基本指令以及数据类型
  • 原文地址:https://www.cnblogs.com/George1123/p/12611937.html
Copyright © 2011-2022 走看看