zoukankan      html  css  js  c++  java
  • AGC030 简要题解

    题目链接

    第一次尝试做一整场AGC,结果做了一整天。。。还是开个博客记录一下吧。

    A - Poisonous Cookies

    一道难度评分高达90分的思博题。

    #include<bits/stdc++.h>
    using namespace std;
    int a,b,c; 
    int main(){
    	scanf("%d%d%d",&a,&b,&c);
    	printf("%d",b+min(c,a+b+1));
    	return 0;
    }
    

    B - Tree Burning

    一开始以为可以直接顺时针走一个,逆时针走一个,然后顺时针再走一个,逆时针再走一个,以此类推。然后观察样例可以发现,还可能会先沿某一个方向走若干个点,再开始上面的循环过程,这个贪心做法可以用数学归纳法证明是正确的。

    于是接下来就枚举走到哪个点时开始循环,循环后的部分可以通过预处理前缀和与后缀和做到 (mathcal O(n))

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=2e5+10;
    int L,n,x[N];
    ll suf[N],pre[N];
    int main(){
    	scanf("%d%d",&L,&n);
    	for(int i=1;i<=n;++i) scanf("%d",&x[i]);
    	ll ans=0;
    	for(int i=1;i<=n;++i) pre[i]=pre[i-1]+x[i];
    	for(int i=n;i>=1;--i) suf[i]=suf[i+1]+L-x[i];
    	for(int i=1;i<=n;++i){
    		ll now=0;
    		if((n-i+1)&1){
    			int t=n-(n-i)/2+1;
    			now+=2ll*suf[t]+2ll*(pre[t-1]-pre[i-1])-x[t-1];
    		}
    		else{
    			int t=n-(n-i+1)/2+1;
    			now+=2ll*suf[t]+2ll*(pre[t-1]-pre[i-1])-(L-x[t]); 
    		}
    		if(i==n) now=x[n];
    		ans=max(ans,now);
    	}
    	for(int i=n;i>=1;--i){
    		ll now=0;
    		if(i&1){
    			int t=i/2;
    			now+=2ll*pre[t]+2ll*(suf[t+1]-suf[i+1])-(L-x[t+1]);
    		}
    		else{
    			int t=i/2;;
    			now+=2ll*pre[t]+2ll*(suf[t+1]-suf[i+1])-x[t]; 
    		}
    		if(i==1) now=L-x[1];
    		ans=max(ans,now);
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    C - Coloring Torus

    神仙构造题。

    如果 (kle 500) 那么一个容易想到的做法是对每一行/每一列填同一个颜色,这样一定合法。考虑将这一个构造改装到对角线上(对角线是指 (i+jmod n) 相同的点)。但这样做依然只能染 (n) 个颜色。

    如何变成 (2n) 个颜色呢?考虑对于每个对角线,给它分配两种颜色交替出现,比如下图中的 (2,5) 两种颜色,就可以通过此题。

    1 2 3 4
    2 3 5 1
    3 4 1 2
    5 1 2 3
    
    #include<bits/stdc++.h>
    using namespace std;
    const int N=510;
    int k,n,a[N][N];
    int main(){
    	scanf("%d",&k);
    	if(k<500){
    		n=k;
    		printf("%d
    ",n);
    		for(int i=1;i<=k;puts(""),++i) for(int j=1;j<=k;++j) printf("%d ",i);
    		return 0;
    	}
    	else{
    		n=500;k-=n;
    		for(int i=1;i<=n;++i){
    			int ta=i,tb=i;
    			if(k) tb=n+i,k--;
    			for(int x=1,y=i;x<=n;++x,y=y%n+1)
    				a[x][y]=x&1?ta:tb;
    		}
    		printf("%d
    ",n);
    		for(int i=1;i<=n;puts(""),++i) for(int j=1;j<=n;++j) printf("%d ",a[i][j]);
    	}
    	return 0;
    }
    

    D - Inversion Sum

    由于 (n) 很小,考虑直接对于位置 (a_i,a_j) 计算有多少种方案使得它们最终对答案做出贡献。

    考虑倒着模拟整个过程,记 (dp_{i,j}) 表示所选择的两个数当前分别在位置 (i)(j) 上时,有多少种方案使得最终位置满足前一个数大于后一个数。初始 (dp_{i,j}=[i>j])。然后倒着扫一遍所有交换操作 ((x,y)),可以发现我们只用对包含 (x,y)(mathcal O(n)) 个状态进行转移,这样一来复杂度就是 (mathcal O(nq)) 的了。

    最后再枚举 (a_i,a_j) ,根据它们的大小关系计算对答案的贡献即可。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=3010,mod=1e9+7,iv=(mod+1)/2;
    int n,q,a[N],x[N],y[N],dp[N][N];
    inline void inc(int &x,int y){x=(x+y>=mod)?x+y-mod:x+y;}
    inline int add(int x,int y){return (x+y>=mod)?x+y-mod:x+y;}
    int main(){
    	scanf("%d%d",&n,&q);
    	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    	for(int i=1;i<=q;++i) scanf("%d%d",&x[i],&y[i]);
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<i;++j)
    			dp[i][j]=1;
    	for(int i=q;i>=1;--i){
    		int xx=x[i],yy=y[i];
    		for(int j=1;j<=n;++j){
    			if(j!=xx&&j!=yy){
    				int a=dp[xx][j],b=dp[yy][j];inc(a,b);
    				dp[xx][j]=dp[yy][j]=1ll*a*iv%mod;
    				a=dp[j][xx],b=dp[j][yy];inc(a,b);
    				dp[j][xx]=dp[j][yy]=1ll*a*iv%mod;
    			}
    		}
    		int a=dp[xx][yy],b=dp[yy][xx];inc(a,b);
    		dp[xx][yy]=dp[yy][xx]=1ll*a*iv%mod;
    	}
    	int ans=0;
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=n;++j){
    			if(i==j) continue; 
    			if(a[i]<a[j]) ans=add(ans,dp[i][j]);
    			else if(a[i]>a[j]) ans=add(ans,(mod+1-dp[i][j])%mod);
    		}
    	for(int i=1;i<q;++i) ans=add(ans,ans);
    	printf("%d
    ",ans);
    	return 0;
    }
    

    E - Less than 3

    神仙题,E比F难系列。考虑在序列上的 (0,1) 之间连一条红线,在 (1,0) 之间连一条蓝线,在序列的最左侧与最右侧分别增加若干条红蓝交替的线,那么一次操作就相当于将一条线左移或者右移,移动过程中要求不能有相邻的两条线的距离 (>2),下面是官方题解的图片:

    于是我们只需要找一个 (s) 中的线与 (t) 中线的对应关系。找到关系后只需要固定位置不变的线,将其他线分成多个块,每个块内部向同一个方向移动即可完成构造。于是我们只需要 (mathcal O(n)) 的枚举对应方案即可做到 (mathcal O(n^2))

    #include<bits/stdc++.h>
    using namespace std;
    const int N=5010;
    char s[N],t[N];
    int n,a[N],b[N],c1,c2;
    int ans=0x3f3f3f3f;
    int main(){
    	scanf("%d",&n);
    	scanf("%s%s",s+1,t+1);
    	for(int i=1;i<n;++i) if(s[i]!=s[i+1]) a[++c1]=i;
    	for(int i=1;i<n;++i) if(t[i]!=t[i+1]) b[++c2]=i;
    	for(int i=-c2;i<=c1+1;++i){
    		if((i&1)&&s[1]!=t[1]) continue;
    		if(i%2==0&&s[1]==t[1]) continue; 
    		int now=0;
    		for(int j=min(1,i),k=min(1-(i-1),1);j<=c1||k<=c2;++j,++k){
    			int x=(j<1)?0:(j>c1?n:a[j]);
    			int y=(k<1)?0:(k>c2?n:b[k]);
    			now+=abs(x-y);
    		}
    		ans=min(ans,now);
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    F - Permutation and Minimum

    考虑将原问题看成将 (1sim 2n) 的排列分成 (n) 对数。两个数已经确定的数对可以忽略,剩下还有确定了一个数以及一个都没有确定的数对。考虑 (dp),从小到大填入每一个数,设 (dp_{i,j,k}) 表示填了 (le i) 的数,当前已确定最小值的有 (j) 对(设为 (A) 类数对), 已填一个数但不确定大小的有 (k) 个(设为 (B) 类数对)。

    转移需要注意的是,如果将一个数放进 (B) 类数对,那我们无法确定是与哪一个数进行的匹配,可以将操作延后至遍历到与它配对的数时进行。遍历到某个被钦定了位置的数 (x) 时,如果还有 (suf) 个钦定数没有配对,那么就意味着有 (suf-k) 个前面的数与这些钦定的数进行匹配,因此可以讨论 (x) 是否与前面匹配,有转移:

    [dp_{i+1,j,k}gets (suf-k)dp_{i,j,k}\ dp_{i+1,j+1,k-1}gets dp_{i,j,k} ]

    于是复杂度为 (mathcal O(n^3))

    #include<bits/stdc++.h>
    using namespace std;
    const int N=610,mod=1e9+7;
    int dp[N][310][310];//考虑到第i个数字,当前已确定最小值的有j个, 已填一个数但未确定大小的有j个 
    int n,a[N],d[N],tot,c1,c2,pd[N],vis[N],inv[N];
    inline int dec(int x,int y){return (x-y<0)?x-y+mod:x-y;}
    inline void inc(int &x,int y){x=(x+y>=mod)?x+y-mod:x+y;}
    int main(){
    	scanf("%d",&n);n<<=1;
    	for(int i=1;i<=n;++i){
    		scanf("%d",&a[i]);
    		if(a[i]>0) pd[a[i]]=1;
    	}
    	inv[0]=1;
    	for(int i=1;i<=n;++i) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    	for(int i=1;i<=n;i+=2){
    		if(a[i]>0&&a[i+1]>0){vis[a[i]]=vis[a[i+1]]=1;continue;}
    		else if(a[i]>0||a[i+1]>0) ++c2;
    		else ++c1;
    	}
    	for(int i=1;i<=n;++i) if(!vis[i]) d[++tot]=i;
    	dp[0][0][c2]=1;
    	int suf=c2;
    	for(int i=0;i<tot;++i){
    		for(int j=0;j<=c1+c2;++j)
    			for(int k=0,x;k<=c2;++k){
    				if(!(x=dp[i][j][k])) continue;
    				if(pd[d[i+1]]){
    					if(k!=suf) inc(dp[i+1][j][k],1ll*(suf-k)*x%mod);
    					if(k) inc(dp[i+1][j+1][k-1],x);
    				}
    				else{
    					if(j) inc(dp[i+1][j-1][k],x);
    					int tmp=(i-j)/2+j+k;
    					if(c1+c2-tmp) 
    						inc(dp[i+1][j+1][k],x);
    					if(k) inc(dp[i+1][j][k-1],x);
    				}
    			}
    		if(pd[d[i+1]]) suf--;
    	}
    	int ans=dp[tot][0][0];
    	for(int i=1;i<=c1;++i) ans=1ll*ans*i%mod;
    	printf("%d
    ",ans);
    	return 0;
    }
    
    
  • 相关阅读:
    SVG
    JavaScript的性能优化
    sublime长期使用的快捷键
    spring实现AOP
    java之spring
    JAVA面试题02
    java面试题01
    MyBatis之关联关系
    MyBatis之动态sql
    MyBatis之sql映射文件
  • 原文地址:https://www.cnblogs.com/tqxboomzero/p/15477839.html
Copyright © 2011-2022 走看看