[SDOI2019] 染色

题意:

给定$2 imes n$的格点图。其中一些结点有着已知的颜色,其余的结点还没有被染色。一个合法的染色方案不允许相邻结点有相同的染色。

现在一共有c种不同的颜色,依次记为1到n。请问有多少对未染色结点的合法染色方案?

$100pts:n,cleq 10^{5}$;

$96pts:n,cleq 10^{4}$。

题解:

首先想到一个$O(nc^{2})$的dp,但一分都没有。

而且后面的方案和已经决定的方案有关,于是这题除了dp没有其他做法。

于是考虑优化dp。注意到如果将原图中所有的空白列删掉,我们就可以$O(nc)$做了。

那么我们可以考虑预处理出$g(i,j,k)$表示两个非空白列i,j中间的k个空白列对答案的贡献。

不过其实不需要记录$(i,j)$,我们可以按照这四个颜色互相是否相同分出5种等价类,大概是

  1. $left[ egin{matrix} x & cdots & x\ y & cdots & y end{matrix} ight]$
  2. $left[ egin{matrix} x & cdots & y\ y & cdots & x end{matrix} ight]$
  3. $left[ egin{matrix} x & cdots & z\ y & cdots & y end{matrix} ight]$或$left[ egin{matrix} x & cdots & x\ y & cdots & z end{matrix} ight]$
  4. $left[ egin{matrix} x & cdots & z\ y & cdots & x end{matrix} ight]$或$left[ egin{matrix} x & cdots & y\ y & cdots & z end{matrix} ight]$
  5. $left[ egin{matrix} x & cdots & z\ y & cdots & w end{matrix} ight]$

显然每个等价类里的$(i,j,k)$算出来是相等的,那么只需要维护$g(5,k)$了,推一下转移式子即可。

注意推出来的式子里z,w是不固定的,而原dp转移时这四个数都是固定的,所以还要除一下。

然后回到原dp,对于两个非空白列我们再分类讨论一下有没有无空白列,枚举当前这一列填的数转移即可(之前那列填的数直接整体转移)。

这样可以获得96分的好成绩,然后发现这个dp的转移跟这场比赛的第一题是一样的(伏笔消除),于是复制粘贴再改改就可以100了。

虽然这么说……100也只有石神这种代码能力很强的强者能写出来,我是真的不行了(

套路:

  • 子问题之间互相影响$ ightarrow$一般只有dp做法。
  • 有大量具有相同性质的元素$ ightarrow$合并计算以降低复杂度。

代码:

#include<bits/stdc++.h>
#define maxn 10005
#define maxm 500005
#define inf 0x7fffffff
#define mod 1000000009
#define ll long long
#define rint register int
#define debug(x) cerr<<#x<<": "<<x<<endl
#define fgx cerr<<"--------------"<<endl
#define dgx cerr<<"=============="<<endl

using namespace std;
ll g[maxn][5],A[maxn][2],dp[maxn],tp[maxn],pw[maxn];

inline ll read(){
    ll x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}

inline bool isw(ll x){return (A[x][0]!=0)&&(A[x][1]!=0);}
inline bool isz(ll x){return (A[x][0]==0)&&(A[x][1]==0);}

inline ll power(ll a,ll b){
    ll ans=1;
    while(b){
        if(b&1) ans=ans*a%mod;
        a=a*a%mod,b>>=1;
    }    
    return ans;
}

inline ll chk(ll a,ll b,ll c,ll d){
    if(a==c && b==d) return 0;
    else if(a==d && b==c) return 1;
    else if(a==c || b==d) return 2;
    else if(a==d || b==c) return 3;
    else return 4;
}

inline void init(ll n,ll c){
    g[0][0]=1;
    int nxt[5][5]={
    {0,1,0,c-2,(c-2)*(c-3)%mod},
    {1,0,c-2,0,(c-2)*(c-3)%mod},
    {0,2,c-2,2*c-5,2*(c-3)*(c-3)%mod},
    {2,0,2*c-5,c-2,2*(c-3)*(c-3)%mod},
    {1,1,c-3,c-3,((c-4)*(c-4)%mod+c-3)%mod}};
    for(rint i=1;i<=n;i++)
        for(rint j=0;j<5;j++)
            for(rint k=0;k<5;k++)
                g[i][j]=(g[i][j]+g[i-1][k]*nxt[k][j]%mod)%mod;
    ll iv1=power(c-2,mod-2),iv2=power((c-2)*(c-3)%mod,mod-2);
    for(rint i=1;i<=n;i++){
        g[i][2]=g[i][2]*iv1%mod;
        g[i][3]=g[i][3]*iv1%mod;
        g[i][4]=g[i][4]*iv2%mod;
    }
}

int main(){
    ll n=read(),c=read(),sum=0; init(n,c);
    for(rint i=1;i<=n;i++) A[i][0]=read();
    for(rint i=1;i<=n;i++) A[i][1]=read();
    pw[0]=1; for(rint i=1;i<=n;i++) pw[i]=pw[i-1]*((c-2)*(c-2)%mod+c-1)%mod;
    int p=1; while(isz(p) && p<=n) p++;
    if(isw(p)) dp[0]=tp[0]=sum=pw[p-1];
    else for(rint k=1;k<=c;k++){
        if(k==(A[p][0]?A[p][0]:A[p][1])) continue;
        dp[k]=tp[k]=pw[p-1],sum=(sum+dp[k])%mod;
    }
    for(rint i=p,j=p+1;i<=n,j<=n;i=j,j=i+1){
        while(isz(j) && j<=n) j++;
        if(j>n){for(rint k=0;k<=c;k++) dp[k]=dp[k]*pw[n-i]%mod;break;}
        if(isw(i) && isw(j)){
            memset(dp,0,sizeof(dp)),sum=0;
            int type=chk(A[i][0],A[i][1],A[j][0],A[j][1]);
            dp[0]=tp[0]*g[j-i][type]%mod,sum=dp[0];
            memcpy(tp,dp,sizeof(dp));
        }
        else if(isw(i)){
            memset(dp,0,sizeof(dp)),sum=0;
            for(rint k=1;k<=c;k++){
                if(k==(A[j][0]?A[j][0]:A[j][1])) continue;
                int type=chk(A[i][0],A[i][1],A[j][0]?A[j][0]:k,A[j][1]?A[j][1]:k);
                dp[k]=tp[0]*g[j-i][type]%mod,sum=(sum+dp[k])%mod;
            }
            memcpy(tp,dp,sizeof(dp));
        }
        else if(isw(j)){
            memset(dp,0,sizeof(dp)),sum=0;
            for(rint k=1;k<=c;k++){
                int type=chk(A[i][0]?A[i][0]:k,A[i][1]?A[i][1]:k,A[j][0],A[j][1]);
                dp[0]=(dp[0]+tp[k]*g[j-i][type]%mod)%mod,sum=dp[0];
            }
            memcpy(tp,dp,sizeof(dp));
        }
        else{
            memset(dp,0,sizeof(dp));
            for(rint k=1;k<=c;k++){
                ll a=A[i][0]?A[i][0]:A[i][1],b=A[j][0]?A[j][0]:A[j][1],ts=sum;
                if(k==b) continue;
                if(k!=a){
                    int kk=k,type=chk(A[i][0]?A[i][0]:kk,A[i][1]?A[i][1]:kk,A[j][0]?A[j][0]:k,A[j][1]?A[j][1]:k);
                    dp[k]=(dp[k]+tp[kk]*g[j-i][type]%mod)%mod,ts=(ts-tp[kk]+mod)%mod;
                }
                if(b!=a){
                    int kk=b,type=chk(A[i][0]?A[i][0]:kk,A[i][1]?A[i][1]:kk,A[j][0]?A[j][0]:k,A[j][1]?A[j][1]:k);
                    dp[k]=(dp[k]+tp[kk]*g[j-i][type]%mod)%mod,ts=(ts-tp[kk]+mod)%mod;
                }
                int type=chk(A[i][0]?A[i][0]:inf,A[i][1]?A[i][1]:inf,A[j][0]?A[j][0]:k,A[j][1]?A[j][1]:k);
                dp[k]=(dp[k]+ts*g[j-i][type]%mod)%mod;
            }
            sum=0; for(rint k=1;k<=c;k++) sum=(sum+dp[k])%mod;
            memcpy(tp,dp,sizeof(dp));
        }
    }
    ll ans=0;
    for(rint i=0;i<=c;i++) ans=(ans+dp[i])%mod;
    printf("%lld
",ans);
    return 0;
}
染色
原文地址:https://www.cnblogs.com/YSFAC/p/13091252.html