• AtCoder Grand Contest 030题解


    昨天随便找了个(AGC)肝一肝,结果发现自己啥都不会……再看了看当初的比赛结果人家全都是俩小时近乎(AK)的……

    (A-Poisonous Cookies)

    cout<<b+min(c,a+b+1);
    

    (B - Tree Burning)

    不难发现中间肯定有一个断点,满足它后面的全都是顺时针走过去的,它前面的全是逆时针走过去的。而且第一次走了之后,之后每一次都反向肯定最优。那么我们枚举一下断点,前缀和优化就好了

    //minamoto
    #include<bits/stdc++.h>
    #define R register
    #define ll long long
    #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;}
    using namespace std;
    char buf[1<<21],*p1=buf,*p2=buf;
    inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
    int read(){
        R int res,f=1;R char ch;
        while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
        for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
        return res*f;
    }
    const int N=2e5+5;
    int n,L,a[N];ll res,sum[N],suf[N];
    int main(){
    //	freopen("testdata.in","r",stdin);
    	L=read(),n=read();
    	fp(i,1,n)a[i]=read(),sum[i]=sum[i-1]+(a[i]<<1);
    	fd(i,n,1)suf[i]=suf[i+1]+((L-a[i])<<1);
    	res=max(a[n],L-a[1]);
    	fp(i,1,n-1){
    		R int k=min(i,n-i);R ll tmp=sum[i]-sum[i-k]+suf[i+1]-suf[i+1+k];
    		cmax(res,tmp-min(a[i],L-a[i+1]));
    		i>k?cmax(res,tmp+(a[i-k]<<1)-a[i]):0;
    		n-i>k?cmax(res,tmp+((L-a[i+1+k])<<1)-(L-a[i+1])):0;
    //		printf("%d %lld
    ",i,tmp);
    	}
    	printf("%lld
    ",res);
    	return 0;
    }
    

    (C - Coloring Torus)

    首先有个构造方法,行列标号为(0)(n-1)之后把所有(i+j)(n)同余的格子标为同一种颜色(即每一条副对角线上填同一种颜色),易证显然合法

    然而这样的话最多只能填(500)个,不够。我们考虑一条副对角线上填两种颜色,两种颜色交替,然而因为一条副对角线上的元素为(n)个,那么此时(n)必须为偶数才能合法。顺便注意可能不一定填满,也就是说需要某些副对角线上两种颜色,某些一种颜色,判一下就好了

    //minamoto
    #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)
    using namespace std;
    char buf[1<<21],*p1=buf,*p2=buf;
    inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
    int read(){
        R int res,f=1;R char ch;
        while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
        for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
        return res*f;
    }
    char sr[1<<21],z[20];int C=-1,Z=0;
    inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
    void print(R int x){
        if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
        while(z[++Z]=x%10+48,x/=10);
        while(sr[++C]=z[Z],--Z);sr[++C]=' ';
    }
    const int N=505;
    int n,k,c,p,mp[N][N];
    void solve(int p,int c){
    	for(R int i=0,j=(n+p-i)%n;i<n;++i,j=(n+p-i)%n)
    		mp[i][j]=c+(i&1);
    }
    void dd(int p,int c){
    	for(R int i=0,j=(n+p-i)%n;i<n;++i,j=(n+p-i)%n)
    		mp[i][j]=c;
    }
    int main(){
    	scanf("%d",&k),n=(k+1)>>1,n+=(n&1);
    	if(k==1)return puts("1
    1
    "),0;
    	p=0,c=1;
    	while(n-p<k-c+1)solve(p,c),++p,++c,++c;
    	while(p<n&&c<=k)dd(p,c),++p,++c;
    	print(n),sr[C]='
    ';
    	fp(i,0,n-1){
    		fp(j,0,n-1)print(mp[i][j]);
    		sr[C]='
    ';
    	}
    	return Ot(),0;
    }
    

    (D - Inversion Sum)

    我们枚举数对(i,j(i<j)),此时我们需要求出(q)次之后(a_i)能和(a_j)构成逆序对的方案数有多少种。那么所有数对((i,j))的方案数加起来就是答案了

    (a_x>a_y)为例,那么我们需要(q)次转移之后(x)排在(y)的前面。我们设(f_{t,i,j})表示(t)次转移之后(a_x)(i)位置,(a_y)(j)位置的方案数,初值为(f_{0,x,y}=1),其余全为(f_{0,i,j})全为(0)。转移为(下面转移中这个(x,y)指的是第(t)次可以交换的两个数(x,y)

    [f_{t,i,j}=2f_{t-1,i,j}(i eq x and j eq y) ]

    [f_{t,x,j}=f_{t,y,j}=f_{t-1,x,j}+f_{t-1,y,j} ]

    [f_{t,i,x}=f_{t,i,y}=f_{t-1,i,x}+f_{t-1,i,y} ]

    [f_{t,x,y}=f_{t,y,x}=f_{t-1,x,y}+f_{t-1,y,x} ]

    最终答案为(sum _{i<j}f_{q,i,j})

    发现转移一层需要修改的状态是(O(n))的,其它打个(tag)就行,所以转移的总复杂度就是(O(n^2)),算上枚举点对的话总复杂度是(O(n^4))

    让我们看看能不能快速对一个点对求出答案……一次(dp)的本质相当于是求出((0,x,y))这个点到((q,i,j))这个点的路径条数,那么我们可以倒过来,求出((q,i,j))这个点到((0,x,y))的路径条数,只要初始时将所有(f_{q,i,j}(i<j))设为(1),那么倒着推一遍之后(f_{0,x,y})代表的就是到((x,y))的路径条数,这样的话一遍递推就行了

    然后还要考虑(i>j)的情况,那么再递推一遍就行了

    总复杂度为(O(n^2))

    //minamoto
    #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)
    using namespace std;
    char buf[1<<21],*p1=buf,*p2=buf;
    inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
    int read(){
        R int res,f=1;R char ch;
        while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
        for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
        return res*f;
    }
    const int N=3005,P=1e9+7,inv2=500000004;
    inline void Add(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))if(y&1)res=mul(res,x);
    	return res;
    }
    struct node{int x,y;}p[N];
    int f[N][N],g[2][N],ans[2][N][N],a[N];
    int n,q,res;
    void solve(int pos){
    	int tag=1;
    	fd(t,q,1){
    		tag=mul(tag,2);
    		int x=p[t].x,y=p[t].y;
    		fp(i,1,n)g[0][i]=g[1][i]=0;
    		fp(i,1,n)g[0][i]=add(f[x][i],f[y][i]),g[1][i]=add(f[i][x],f[i][y]);
    		int c=f[x][y],d=f[y][x];
    		fp(i,1,n)f[x][i]=f[y][i]=mul(g[0][i],inv2),f[i][x]=f[i][y]=mul(g[1][i],inv2);
    		f[x][y]=f[y][x]=mul(add(c,d),inv2),f[x][x]=f[y][y]=0;
    	}
    	fp(i,1,n)fp(j,1,n)ans[pos][i][j]=mul(f[i][j],tag);
    }
    void init(){
    	fp(i,1,n)fp(j,1,n)f[i][j]=0;
    	fp(i,1,n)fp(j,i+1,n)f[i][j]=1;
    	solve(0);
    	fp(i,1,n)fp(j,1,n)f[i][j]=0;
    	fp(i,1,n)fp(j,1,i-1)f[i][j]=1;
    	solve(1);
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	n=read(),q=read();
    	fp(i,1,n)a[i]=read();
    	fp(i,1,q)p[i].x=read(),p[i].y=read();
    	init();
    	fp(i,1,n)fp(j,i+1,n)Add(res,a[i]==a[j]?0:a[i]>a[j]?ans[0][i][j]:ans[1][i][j]);
    	printf("%d
    ",res);
    	return 0;
    }
    
    

    (E - Less than 3)

    不会了……太神仙了……

    引用一下官方题解的图

    上面的图的意思就是,我们在(01)之间连一条红线,在(10)之间连一条蓝线,那么一次操作如果不在边界上只会导致线的移动,如果是在边界上那么可能会导致多出一条线。而且易发现红线蓝线必定交替出现

    我们先在左右两边无限红蓝红蓝延伸下去,那么我们的操作的目的就是使(s,t)的红线蓝线一一对应,一组对应边需要花费的次数恰好就是它们之间的距离

    那么我们枚举一下(s,t)中那几条红线一一对应就行了

    //minamoto
    #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 cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    char buf[1<<21],*p1=buf,*p2=buf;
    inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
    int read(){
        R int res,f=1;R char ch;
        while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
        for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
        return res*f;
    }
    int read(char *s){
    	R int len=0;R char ch;while(((ch=getc())>'9'||ch<'0'));
    	for(s[++len]=ch;(ch=getc())>='0'&&ch<='9';s[++len]=ch);
    	return s[len+1]='',len;
    }
    const int N=10005;
    char s[N],t[N];int a[N],b[N],q[N],st[N],h,top,res;
    int n,m;
    void calc(){
    	top=0,h=0;
    	fp(i,1,n)st[++top]=0;
    	fp(i,1,n-1)!b[i]&&b[i+1]?st[++top]=i:0;
    	fp(i,1,n)st[++top]=n;
    	fp(i,1,n-1)!a[i]&&a[i+1]?q[++h]=i:0;
    	int mn=0x3f3f3f3f,now;
    	fp(i,1,top-n+1){
    		now=0;
    		fp(j,1,i-1)if(st[j])now+=st[j];
    		fp(j,0,h-1)now+=abs(q[j+1]-st[i+j]);
    		for(R int j=h;st[i+j]!=n;++j)now+=n-st[i+j];
    		cmin(mn,now);
    	}
    	res+=mn;
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	n=read(),read(s),read(t);
    	fp(i,1,n)a[i]=s[i]-'0',b[i]=t[i]-'0';
    	if(n==1&&a[1]!=b[1])return puts("1"),0;
    	if(n==2&&a[1]!=b[1]&&a[1]==a[2]&&b[1]==b[2])return puts("2"),0;
    	calc();
    	fp(i,1,n)a[i]^=1,b[i]^=1;
    	calc();
    	printf("%d
    ",res);
    	return 0;
    }
    
    

    (F - Permutation and Minimum)

    还是不会……

    如果有一对全都有数那么不影响答案不考虑,以及设全为(-1)的对数为(cnt),最终方案里这些可以任意互换所以答案要乘上(cnt!)

    正着推啥也推不出来那么我们倒着推,设(f_{i,j,k})表示已经填了所有小于等于(i)的数,其中有(j)个在数列中的数未匹配,(k)个不在数列中的数未匹配,转移可以看代码,注意如果匹配一个出现过的数有(j)种匹配方法

    //minamoto
    #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)
    using namespace std;
    char buf[1<<21],*p1=buf,*p2=buf;
    inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
    int read(){
        R int res,f=1;R char ch;
        while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
        for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
        return res*f;
    }
    int read(char *s){
    	R int len=0;R char ch;while(((ch=getc())>'9'||ch<'0'));
    	for(s[++len]=ch;(ch=getc())>='0'&&ch<='9';s[++len]=ch);
    	return s[len+1]='',len;
    }
    const int N=1005,P=1e9+7;
    inline void Add(R int &x,R int y){(x+=y)>=P?x-=P:0;}
    inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
    bool vis[N],nt[N];int a[N],q[N],f[605][305][305];
    int n,m,t,cnt,ans;
    int main(){
    //	freopen("testdata.in","r",stdin);
    	n=read();
    	fp(i,1,n<<1){
    		a[i]=read(),a[i]!=-1?vis[a[i]]=1:0;
    		if(i&1^1){
    			a[i]==-1&&a[i-1]==-1?++cnt:0;
    			a[i]!=-1&&a[i-1]!=-1?nt[a[i]]=nt[a[i-1]]=1:0;
    		}
    	}
    	fp(i,1,n<<1)!nt[i]?(vis[i]?q[++t]=1:q[++t]=0):0;
    	n=t;
    	f[n][0][0]=1;
    	fd(i,n,1){
    		fp(j,0,n>>1)fp(k,0,n>>1)if(f[i][j][k]){
    			R int v=f[i][j][k];
    			if(q[i]){
    				Add(f[i-1][j+1][k],v);
    				k?(Add(f[i-1][j][k-1],v),0):0;
    			}else{
    				Add(f[i-1][j][k+1],v);
    				k?(Add(f[i-1][j][k-1],v),0):0;
    				j?(Add(f[i-1][j-1][k],mul(v,j)),0):0;
    			}
    		}
    	}
    	ans=f[0][0][0];
    	fp(i,2,cnt)ans=mul(ans,i);
    	printf("%d
    ",ans);
    	return 0;
    }
    
    
  • 相关阅读:
    2020阿里最新出品的泰山版Java开发手册,告别垃圾代码
    freecplus框架-目录操作
    freecplus框架简介
    freecplus框架-加载参数文件
    freecplus框架-xml解析
    freecplus框架-tcp网络通信
    freecplus框架-PostgreSQL数据库操作
    freecplus框架-Oracle数据库操作
    freecplus框架-MySQL数据库操作
    freecplus框架-ftp客户端
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/10527862.html
Copyright © 2020-2023  润新知