• AtCoder Grand Contest 039 题解


    传送门

    (A)

    首先只有一串的情况下,遇到相同的肯定是改后面那一个最优,然后两串的话可能要分奇偶讨论一下

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    const int N=105;
    typedef long long ll;
    char s[N];int t[N],n,k;ll res,sum;
    int main(){
    	scanf("%s%d",s+1,&k),n=strlen(s+1);
    	fp(i,1,n)t[i]=s[i];
    	fp(i,2,n)if(t[i]==t[i-1])++res,t[i]=2333;
    	if(t[n]==t[1]){
    		fp(i,1,n)t[i]=s[i];t[1]=2333,++sum;
    		fp(i,2,n)if(t[i]==t[i-1])++sum,t[i]=2333;
    		if(t[n]==2333)return printf("%lld
    ",res*((k+1)>>1)+sum*(k>>1)),0;
    		return printf("%lld
    ",res+sum*(k-1)),0;
    	}
    	res*=k;
    	printf("%lld
    ",res);
    	return 0;
    }
    

    (B)

    首先有奇环肯定无解,否则我们枚举哪个点是(1)号点,用(bfs)依次确定剩下的每个点的编号,因为图中不存在奇环所以这样跑出来的肯定合法

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    const int N=205,M=5e5+5;
    struct eg{int v,nx;}e[M];int head[N],tot;
    inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
    char s[N];int vis[N],ins[N],col[N],q[N],cnt,tim,n,mx,fl;
    void dfs(int u,int d){
    	col[u]=d,ins[u]=1;
    //	printf("qwq %d %d %d
    ",u,d,col[u]);
    	go(u)if(!ins[v])dfs(v,d^1);
    		else if(col[v]!=(d^1))fl=1;
    }
    void bfs(R int s){
    	R int h=1,t=0,u;
    	++tim,q[++t]=s,vis[s]=tim,col[s]=1;
    	while(h<=t){
    		u=q[h++];
    		go(u)if(vis[v]!=tim)vis[v]=tim,col[v]=col[u]+1,q[++t]=v;
    			else assert(abs(col[u]-col[v])==1);
    	}
    	cmax(mx,col[q[t]]);
    }
    int main(){
    	scanf("%d",&n);
    	fp(i,1,n){
    		scanf("%s",s+1);
    		fp(j,1,n)if(s[j]=='1')add(i,j);
    	}
    	dfs(1,0);if(fl)return puts("-1"),0;
    	fp(i,1,n)bfs(i);
    	printf("%d
    ",mx);
    	return 0;
    }
    

    (C)

    把操作放到二进制意义下考虑,就是每次把最低位取反然后放到最高位,反过来就可以看成是把最高位取反放到最低位,不难发现任何一个数最多(2n)次之后必定会变回原数

    那么每一个数最少需要的次数(k)肯定是(2n)的因子,鉴于直接计算很麻烦,我们计算(k)次之后相等的数的个数,然后容斥即可得到最少(k)次之后相等的个数

    对于一个(n)位的二进制数(S),我们把(S)按位取反之后接在后面得到一个长为(2n)的数,那么(S)(k)次操作之后相等当且仅当这个长为(2n)的数以(k)为一个周期且({2nover k})必须是奇数
    (如果({2nover k})是偶数说明(S)和它按位取反之后的那个数相等了,显然不可能)

    进一步考虑之后我们发现,整个(S)必定是由一个长为({kover 2})的串(T)和它的按位取反轮流拼接而成的,所以我们只要数出合法的(T)的个数即可

    由于还需要字典序小于(X),那么我们考虑(X)的前({kover 2})位,只要(T)的字典序小于它那么后面必定小于,顺便特判一下(T)(X)的前({kover 2})位相等的情况就行了

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    const int P=998244353;
    inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
    inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
    inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
    inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
    int ksm(R int x,R int y){
    	R int res=1;
    	for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
    	return res;
    }
    const int N=5e5+5;
    char s[N];int a[N],b[N],c[N],st[N],n,res,tot,t;
    int main(){
    //	freopen("testdata.in","r",stdin);
    	scanf("%d%s",&n,s+1);
    	fp(i,1,n)a[i]=s[i]-'0';
    	n<<=1;
    	fp(k,1,n)if(n%k==0&&((n/k)&1)){
    		++tot,b[tot]=k,c[tot]=0;
    		fp(i,1,k>>1)c[tot]=((c[tot]<<1ll)+a[i])%P;
    		fp(i,1,k>>1)st[i]=a[i];
    		fp(i,(k>>1)+1,n>>1)st[i]=st[i-(k>>1)]^1;
    		++c[tot];
    		fp(i,(k>>1)+1,n>>1)if(a[i]!=st[i]){
    			c[tot]-=(a[i]<st[i]);
    			break;
    		}
    	}
    	fp(i,1,tot)fp(j,1,i-1)if(b[i]%b[j]==0)upd(c[i],P-c[j]);
    	fp(i,1,tot)upd(res,mul(c[i],b[i]));
    	printf("%d
    ",res);
    	return 0;
    }
    

    (D)

    数学太差,没有办法……

    前置芝士(1)

    假设圆上的三点分别为(A,B,C),三角形(ABC)的内心为(O),令(A')(AO)与单位圆的另一个交点(不难发现(A')也是(BC)这一段弧的中点),同理定义(B',C'),则三角形(A'B'C')的垂心与(O)重合

    证明:自行画图理解

    前置芝士(2)

    对于任意一个三角形,它的重心(G),垂心(H),外心(O)三点共线,且(2|GO|=|GH|)

    证明:自行百度"欧拉线"

    题解

    因为所有的点都在单位圆上,那么(ABC)的内心即为(A'B'C')的垂心

    而因为(A'B'C')的外心就是原点,重心就是三个点的坐标的平均值,那么我们只要算出重心的期望,就可以推出垂心的期望了。而(A')的坐标只和(BC)有关,与(A)无关(只要保证(A)不在(A')这段弧上即可),那么我们枚举(BC),计算对应的(A')的贡献就行了

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    const int N=3005;double Pi=acos(-1.0);
    double x[N],y[N],rx,ry,tmp;int a[N],n,L;
    int main(){
    //	freopen("testdata.in","r",stdin);
    	scanf("%d%d",&n,&L);Pi*=2.0/L;
    	fp(i,1,n)scanf("%d",&a[i]);
    	fp(i,1,n)fp(j,1,i-1){
    		R int t=i-j-1;
    		if(t){
    			rx+=t*-cos((a[i]+a[j])*0.5*Pi);
    			ry+=t*-sin((a[i]+a[j])*0.5*Pi);
    		}
    		t=n-i+j-1;
    		if(t){
    			rx+=t*cos((a[i]+a[j])*0.5*Pi);
    			ry+=t*sin((a[i]+a[j])*0.5*Pi);
    		}
    	}
    	tmp=1.0*n*(n-1)*(n-2)/2;
    	rx/=tmp,ry/=tmp;
    	rx*=3,ry*=3;
    	printf("%.10lf %.10lf
    ",rx,ry);
    	return 0;
    }
    

    (E)

    好迷的题目啊……

    首先把环从(2n)(1)那里断开变成一条链,然后假设与(1)配对的点是(i)

    那么我们接下来就要对([2,i)cup (i,n])之间的点继续配对,因为边要构成一棵树,所以跨过两个区间的边至少要有一条,且如果有多条时端点要单调

    枚举最靠外侧的横跨两个区间的边((j,k)),然后先考虑((j,i))之间的点,它们连出的边要么和(j)连出的这条边相连,要么和(i)连出的这条边相连,且一定存在一个分界点(p),由于((j,k))是最靠外侧的横跨区间的边,所以对于([2,p])之间的点我们不需要再知道([i,n])的信息,那么可以递归为一个([2,j)cup (j,p])的子问题

    同理定义((i,k))的分界点(q),那么后面这个可以递归为一个([q,k)cup (k,n])的子问题。而中间那部分就是一个([p+1,i)cup (i,q-1])的子问题

    那么(dp)就行了,复杂度(O(n^7))

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    typedef long long ll;
    const int N=55;
    char s[N][N];ll f[N][N][N];int n;
    inline ll calc(R int l,R int r,R int m){
    	if(~f[l][r][m])return f[l][r][m];
    	if(l==r)return f[l][r][m]=1;
    	if(l==m||r==m)return f[l][r][m]=0;
    	R ll res=0;
    	fp(j,l,m-1)fp(k,m+1,r)if(s[j][k]=='1')
    		fp(p,j,m-1)fp(q,m+1,k)
    			res+=calc(l,p,j)*calc(p+1,q-1,m)*calc(q,r,k);
    	return f[l][r][m]=res;
    }
    int main(){
    	memset(f,-1,sizeof(f));
    	scanf("%d",&n);
    	fp(i,1,n<<1)scanf("%s",s[i]+1);
    	R ll res=0;
    	fp(i,2,n<<1)if(s[1][i]=='1')res+=calc(2,n<<1,i);
    	printf("%lld
    ",res);
    	return 0;
    }
    

    (F)

    好玄学的(dp)……

    首先,对于一个矩阵(B),它的权值等价于重新填一个矩阵(A),且(A[i][j])要小于等于(B[i][j])对应位置上的那(n+m-1)个值中的最小值的方案数,等价于(A)中每行的最大值小于等于(B)中对应行的最小值,(A)中每列的最大值小于等于(B)中对应列的最小值,的方案数

    那么最终答案可以转化为所有合法的矩阵(A,B)的个数

    我们记(X_i)(A)中第(i)行的最大值,(Y_i)(B)中第(i)列的最小值,记(dp[i][j][k])表示已经考虑完了所有(X_ileq k)(i)行,(Y_ileq k)(j)列,此时的方案总数,转移分两步

    第一步,枚举(X_i=k+1)的行数,那么对于每一个这样的行,在矩阵(B)已经考虑完(Y_ileq k)的那(j)列中,显然是可以任取(geq k+1)的数,而在矩阵(A)还没有考虑的那(m-j)列中,显然是可以任取(leq k+1)的数,且得保证至少有一个(k+1)

    第二步,枚举(Y_i=k+1)的列数,那么对于每一个这样的列,在矩阵(B)已经考虑完(X_ileq k+1)的那(i)行中,显然是可以任取(geq k+1)的数,且至少得有一个(k+1),在矩阵(A)还没有考虑(X_ileq k+1)的那(n-i)行中,可以任取(leq k+1)的数

    那么额外记一个(0/1)表示转移的两步,预处理转移系数,时间复杂度为(O(nmk(n+m)))

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    int P;
    inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
    inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
    inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
    inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
    int ksm(R int x,R int y){
    	R int res=1;
    	for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
    	return res;
    }
    const int N=105;
    int f[N][N][N][2],coef[N][N][N][2],bn[N][N];
    int n,m,L,ret,cp;
    int main(){
    	scanf("%d%d%d%d",&n,&m,&L,&P);
    	fp(i,0,max(n,m)){
    		bn[i][0]=1;
    		fp(j,1,i)bn[i][j]=add(bn[i-1][j],bn[i-1][j-1]);
    	}
    	fp(k,1,L)fp(i,0,m){
    		cp=mul(ksm(L-k+1,i),dec(ksm(k,m-i),ksm(k-1,m-i)));
    		ret=1;
    		fp(j,0,n)coef[k][i][j][0]=ret,ret=mul(ret,cp);
    	}
    	fp(k,1,L)fp(i,0,n){
    		cp=mul(dec(ksm(L-k+1,i),ksm(L-k,i)),ksm(k,n-i));
    		ret=1;
    		fp(j,0,m)coef[k][i][j][1]=ret,ret=mul(ret,cp);
    	}
    	f[1][0][0][0]=1;
    	fp(k,1,L)fp(i,0,n)fp(j,0,m){
    		ret=f[k][i][j][0];
    		fp(l,0,n-i)upd(f[k][i+l][j][1],1ll*ret*bn[n-i][l]%P*coef[k][j][l][0]%P);
    		ret=f[k][i][j][1];
    		fp(l,0,m-j)upd(f[k+1][i][j+l][0],1ll*ret*bn[m-j][l]%P*coef[k][i][l][1]%P);
    	}
    	printf("%d
    ",f[L+1][n][m][0]);
    	return 0;
    }
    
  • 相关阅读:
    Shell基础:什么是shell脚本、2种脚本解释器、#!约定解释器类型、运行shell脚本的2种方式、shell变量命令规范/赋值/如何使用/只读变量/删除变量/变量类型、shell字符串及其常用方法、shell数组及其常用方法、shell注释
    Linux su命令:su命令语法、su root与su
    docker容器内使用apt报错E: List directory /var/lib/apt/lists/partial is missing.
    浅析事务是什么、mysql是如何实现事务提交和回滚的、保证事务持久性redo log的实现原理、保证事务一致性undo log的实现原理、事务ACID特性及其实现原理
    浅析前后端分离架构下的API安全问题:JWT保证token不被盗用的方案(即如何防范Replay Attacks)
    浅析如何保证缓存与数据库的双写一致性:4种更新缓存的设计模式理解
    浅析SpringCloud中断路器是什么、断路器的作用以及在Feign中使用断路器
    浅析后端微服务涉及到定时任务时如何解决多集群定时任务重复执行并发的方案对比
    Linux连续执行多条命令的写法区别
    Dockerfile中RUN/CMD/ENTRYPOINT命令区别
  • 原文地址:https://www.cnblogs.com/yuanquming/p/11626378.html
Copyright © 2020-2023  润新知