AtCoder Grand Contest 030题解

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

\(A-Poisonous\ Cookies\)

cout<

\(B - Tree\ Burning\)

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

//minamoto
#include
#define R register
#define ll long long
#define fp(i,a,b) for(R int i=(a),I=(b)+1;iI;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
templateinline bool cmax(T&a,const T&b){return a'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\n",i,tmp);
    }
    printf("%lld\n",res);
    return 0;
}

\(C - Coloring\ Torus\)

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

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

//minamoto
#include
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;iI;--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>1,n+=(n&1);
    if(k==1)return puts("1\n1\n"),0;
    p=0,c=1;
    while(n-p

\(D - Inversion\ Sum\)

我们枚举数对\(i,j(i,此时我们需要求出\(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\neq x\ and\ j\neq 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

发现转移一层需要修改的状态是\(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设为\(1\),那么倒着推一遍之后\(f_{0,x,y}\)代表的就是到\((x,y)\)的路径条数,这样的话一遍递推就行了

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

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

//minamoto
#include
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;iI;--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\n",res);
    return 0;
}

\(E - Less\ than\ 3\)

不会了……太神仙了……

引用一下官方题解的图

AtCoder Grand Contest 030题解_第1张图片

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

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

AtCoder Grand Contest 030题解_第2张图片

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

//minamoto
#include
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;iI;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
templateinline 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]='\0',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\n",res);
    return 0;
}

\(F - Permutation\ and\ Minimum\)

还是不会……

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

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

//minamoto
#include
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;iI;--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]='\0',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\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/bztMinamoto/p/10527862.html

你可能感兴趣的:(数据结构与算法)