• AtCoder Grand Contest 038 题解


    传送门

    这场表现的宛如一个(zz)

    (A)

    先直接把前(b)行全写成(1),再把前(a)列取反就行

    const int N=1005;
    char mp[N][N];int n,m,a,b;
    int main(){
    	scanf("%d%d%d%d",&n,&m,&a,&b);
    	fp(i,1,b)fp(j,1,m)mp[i][j]=1;
    	fp(i,1,n)fp(j,1,a)mp[i][j]^=1;
    	fp(i,1,n){
    		fp(j,1,m)putchar(mp[i][j]+'0');
    		putchar('
    ');
    	}
    	return 0;
    }
    

    (B)

    先除去区间内本就有序的情况,那么操作([i,i+k-1])([i+1,i+k])等价当且仅当(p_i)([i,i+k-1])中最小的元素且(p_{i+k})([i+1,i+k])中最大的元素,那么就不计算(i+1)就行了。用单调栈就能判断

    //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=2e5+5;
    int a[N],st[N],up[N],dw[N],dr[N],top,n,res,k,fl;
    int main(){
    	scanf("%d%d",&n,&k);
    	fp(i,1,n)scanf("%d",&a[i]);
    	st[top=0]=0;
    	fp(i,1,n){
    		while(top&&a[i]>a[st[top]])--top;
    		up[i]=(st[top]<=i-k),st[++top]=i;
    	}
    	st[top=0]=n+1;
    	fd(i,n,1){
    		while(top&&a[i]<a[st[top]])--top;
    		dw[i]=(st[top]>=i+k),st[++top]=i;
    	}
    	for(R int l=1,r=2;r<=n;l=r++){
    		while(r<=n&&a[r]>a[r-1])++r;
    		fp(i,l+k-1,r-1)dr[i]=1;
    	}
    	fp(i,k,n)if(dr[i])fl=1;
    		else if(!(up[i]&&dw[i-k]))++res;
    	printf("%d
    ",res+fl);
    	return 0;
    }
    

    (C)

    方便起见设

    [egin{aligned} Ans &=sum_{i=1}^nsum_{j=1}^n ext{lcm}(a_i,a_j) end{aligned} ]

    最后的答案减一减就行了

    然后开始颓柿子

    [egin{aligned} Ans &=sum_{i=1}^nsum_{j=1}^n ext{lcm}(a_i,a_j)\ &=sum_{d=1}^n dsum_{i=1}^nsum_{j=1}^n [d|a_i][d|a_j]{a_iover d}{a_jover d}[gcd({a_iover d},{a_jover d})=1]\ &=sum_{d=1}^n dsum_{i=1}^nsum_{j=1}^n [d|a_i][d|a_j]{a_iover d}{a_jover d}sum_{t|{a_iover d},t|{a_jover d}}mu(t)\ &=sum_{T=1}^n sum_{d|T}dsum_{i=1}^nsum_{j=1}^n [T|a_i][T|a_j]{a_iover T}{a_jover T} imes{T^2over d^2}mu({Tover d})\ &=sum_{T=1}^n sum_{i=1}^nsum_{j=1}^n [T|a_i][T|a_j]{a_iover T}{a_jover T}Tsum_{d|T}{Tover d}mu({Tover d})\ &=sum_{T=1}^n sum_{i=1}^nsum_{j=1}^n [T|a_i][T|a_j]{a_iover T}{a_jover T}Tsum_{d|T}dmu(d)\ end{aligned} ]

    (O(nlog n))预处理(s(T)=sum_{d|T}dmu(d)),然后记(cnt[k])表示(a_i=k)的个数,(O(nlog n))计算即可

    //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=1e6+5,L=1e6;
    bitset<N>vis;int p[N],mu[N],cnt[N],a[N],st[N],c[N],s[N],top,n,m,res;
    void init(int n=L){
    	mu[1]=1;
    	fp(i,2,n){
    		if(!vis[i])p[++m]=i,mu[i]=-1;
    		for(R int j=1;j<=m&&i*p[j]<=n;++j){
    			vis[i*p[j]]=1;
    			if(i%p[j]==0)break;
    			mu[i*p[j]]=-mu[i];
    		}
    	}
    	fp(i,1,n)upd(mu[i],P);
    	fp(i,1,n)for(R int j=i;j<=n;j+=i)upd(s[j],mul(i,mu[i]));
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	scanf("%d",&n);
    	init();
    	fp(i,1,n)scanf("%d",&a[i]),++cnt[a[i]];
    	fp(T,1,L){
    		R int sum=0;
    		for(R int j=T;j<=L;j+=T)upd(sum,mul(j/T,cnt[j]));
    		sum=mul(sum,sum),sum=mul(sum,mul(T,s[T]));
    		upd(res,sum);
    	}
    	fp(i,1,n)res=dec(res,a[i]);
    	res=mul(res,(P+1)>>1);
    	printf("%d
    ",res);
    	return 0;
    }
    

    (D)

    先把(m=n-1)的情况特判掉,接下来只考虑(mgeq n)

    建一张新图(G),对于所有(C_i=0)的关系((a,b))看成(a,b)在同一个连通块里,这样缩完点之后对于每一个(C_i=1)的关系((a,b))必须满足(a,b)在不同连通块里否则无解

    记连通块个数为(k),如果我们从每个连通块中取出一个点,把所有给出的点连成一个环,每个连通块内随便连一棵生成树,这样总的边数为(n),且能保证任意两个处于不同连通块中的点之间至少(2)条路径,这就是边数的下界,且因为之前我们保证了(mgeq n)所以下界必然能达到

    再来考虑上界,每个连通块内连生成树,选出的点之间连完全图,边数就是(n-k+{k (k-1)over 2}=n+{k(k-3)over 2}),那么上界肯定是越大越好,也就是说我们不需要再加额外的边,直接用(G)中的边才能保证(k)最大

    (ps:)对于总的连通块个数为(1)(2)的边界情况都会在判上界的时候处理掉所以没必要讨论

    所以只要(mleq n+{k(k-3)over 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;
    typedef long long ll;
    const int N=1e5+5;
    struct eg{int u,v,c;}e[N];
    ll m,sum;int fa[N],n,q;
    inline int find(R int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    int main(){
    	scanf("%d%lld%d",&n,&m,&q);
    	fp(i,1,n)fa[i]=i;
    	for(R int i=1,u,v;i<=q;++i){
    		scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].c);
    		++e[i].u,++e[i].v;
    		if(!e[i].c)u=find(e[i].u),v=find(e[i].v),fa[u]=v;
    		if(e[i].c&&m==n-1)return puts("No"),0;
    	}
    	if(m==n-1)return puts("Yes"),0;
    	for(R int i=1,u,v;i<=q;++i)if(e[i].c){
    		u=find(e[i].u),v=find(e[i].v);
    		if(u==v)return puts("No"),0;
    	}
    	fp(i,1,n)sum+=(find(i)==i);
    	sum=n-sum+(1ll*sum*(sum-1)>>1);
    	puts(m<=sum?"Yes":"No");
    	return 0;
    }
    

    (E)

    首先,通过(Min-Max)容斥转化一下,问题变成对于一个集合(S),设(S={t_1,t_2,..,t_m}),其中第一个(t_i)出现次数到达(b_{t_i})时的期望步数

    我们设一个状态(x_1,x_2,..,x_m(x_i<b_{t_i})),表示(t_i)恰好出现了(x_i)次,设(P)表示生成的数在集合(S)中的概率,那么这个状态发生变化的期望步数就是({1over P}),同时如果能算出这个状态出现的概率(p),那么它对答案的贡献就是({pover P})了。如果对所有合法的(x)都能计算出贡献,加起来就是集合(S)中第一个(t_i)出现次数到达(b_{t_i})时的期望步数了

    对于(p),设(X=sum_{i=1}^m t_i),则有

    [egin{aligned} p=X!prod_{i=1}^mleft({t_iover P} ight)^{x_i}{1over x_i!} end{aligned} ]

    那么就可以算了,设(f[i][j][k])表示考虑到第(i)个数,其中选的集合中(X=j)(sum a=k)的总的贡献,最后对于每一个合法的状态加上贡献即可

    //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=405;
    int fac[N],ifac[N],bin[N],f[N][N][N],a[N],b[N],n,s1,s2;
    inline void init(R int n=400){
    	fac[0]=ifac[0]=1;fp(i,1,n)fac[i]=mul(fac[i-1],i);
    	ifac[n]=ksm(fac[n],P-2);fd(i,n-1,1)ifac[i]=mul(ifac[i+1],i+1);
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	scanf("%d",&n);
    	init();
    	fp(i,1,n)scanf("%d%d",&a[i],&b[i]);
    	f[0][0][0]=P-1;
    	fp(i,1,n){
    		fp(j,0,s1)fp(k,0,s2)f[i][j][k]=f[i-1][j][k];
    		bin[0]=1;fp(j,1,b[i]-1)bin[j]=mul(bin[j-1],a[i]);
    		fp(j,0,b[i]-1)bin[j]=mul(bin[j],ifac[j]);
    		fp(j,0,s1)fp(k,0,s2)fp(v,0,b[i]-1)
    			upd(f[i][j+v][k+a[i]],P-mul(f[i-1][j][k],bin[v]));
    		s1+=b[i]-1,s2+=a[i];
    	}
    	R int res=0;
    	fp(i,0,s1)fp(j,1,s2){
    		R int ret=mul(f[n][i][j],fac[i]);
    		ret=mul(ret,ksm(j,P-1-i));
    		upd(res,mul(ret,mul(s2,ksm(j,P-2))));
    	}
    	printf("%d
    ",res);
    	return 0;
    }
    

    (F)

    首先,我们从(i)(p_i)连边,那么肯定是形成了若干个环,且环上所有数只能同时选(i)或者(p_i),我们记同时选(i)(1),同时选(p_i)(0)

    同理从(i)(q_i)连边,不过记同时选(i)(0),同时选(q_i)(1)

    然后记(x[i])(y[i])分别表示(i)处于哪个环,那么(i)是否有贡献是跟它所在的两个环的取值以及(p_i)(q_i)的值有关的,根据(p_i)(q_i)的取值讨论一下就行了

    懒得写讨论了直接贴题解(其中(v_i)表示对应的环的取值)

    这东西显然可以转化成一个最小割的模型

    //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)
    #define gg(u) for(int &i=cur[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=5e5+5,M=1e6+5,inf=0x3f3f3f3f;
    inline int min(R int x,R int y){return x<y?x:y;}
    struct eg{int v,nx,w;}e[M];int head[N],tot=1;
    inline void add(R int u,R int v,R int w){
    	e[++tot]={v,head[u],w},head[u]=tot;
    	e[++tot]={u,head[v],0},head[v]=tot;
    }
    int dep[N],cur[N],S,T,n;
    bool bfs(){
    	memset(dep,-1,(T+1)<<2);
    	memcpy(cur,head,(T+1)<<2);
    	static int q[N];
    	int h=1,t=0,u;q[++t]=S,dep[S]=0;
    	while(h<=t){
    		u=q[h++];
    		go(u)if(e[i].w&&dep[v]<0)q[++t]=v,dep[v]=dep[u]+1;
    	}
    	return ~dep[T];
    }
    int dfs(int u,int lim){
    	if(u==T||!lim)return lim;
    	int fl=0,f;
    	gg(u)if(dep[v]==dep[u]+1&&(f=dfs(v,min(lim,e[i].w)))){
    		fl+=f,lim-=f,e[i].w-=f,e[i^1].w+=f;
    		if(!lim)break;
    	}
    	if(!fl)dep[u]=-1;
    	return fl;
    }
    inline int dinic(){R int res=0;while(bfs())res+=dfs(S,inf);return res;}
    int p[N],q[N],blp[N],blq[N],s0[N],s1[N],sum,cnt;
    int main(){
    //	freopen("testdata.in","r",stdin);
    	scanf("%d",&n);
    	fp(i,1,n)scanf("%d",&p[i]),++p[i];
    	fp(i,1,n)scanf("%d",&q[i]),++q[i];
    	fp(i,1,n)if(!blp[i]){
    		++cnt;
    		for(R int j=i;!blp[j];j=p[j])blp[j]=cnt;
    	}
    	fp(i,1,n)if(!blq[i]){
    		++cnt;
    		for(R int j=i;!blq[j];j=q[j])blq[j]=cnt;
    	}
    	S=0,T=cnt+1;
    	fp(i,1,n)sum+=p[i]!=q[i];
    	fp(i,1,n){
    		if(p[i]==q[i]){
    			if(p[i]==i)continue;sum+=2;
    			add(S,blp[i],1),add(blq[i],T,1),add(blp[i],blq[i],2);
    		}else{
    			if(p[i]==i)add(S,blq[i],1);
    			else if(q[i]==i)add(blp[i],T,1);
    			else add(blp[i],blq[i],1);
    		}
    	}
    	printf("%d
    ",sum-dinic());
    	return 0;
    }
    
  • 相关阅读:
    MongoDB存储
    python 查看文件名和文件路径
    Python遍历文件个文件夹
    Python图片缩放
    python opencv
    Python3 关于UnicodeDecodeError/UnicodeEncodeError: ‘gbk’ codec can’t decode/encode bytes类似的文本编码问题
    jmter使用
    HttpRunnerManager使用
    PostMan使用
    工作中的思想
  • 原文地址:https://www.cnblogs.com/yuanquming/p/11565870.html
Copyright © 2020-2023  润新知