[SDOI2019] 染色
题意:
给定$2\times n$的格点图。其中一些结点有着已知的颜色,其余的结点还没有被染色。一个合法的染色方案不允许相邻结点有相同的染色。
现在一共有c种不同的颜色,依次记为1到n。请问有多少对未染色结点的合法染色方案?
$100pts:n,c\leq 10^{5}$;
$96pts:n,c\leq 10^{4}$。
题解:
首先想到一个$O(nc^{2})$的dp,但一分都没有。
而且后面的方案和已经决定的方案有关,于是这题除了dp没有其他做法。
于是考虑优化dp。注意到如果将原图中所有的空白列删掉,我们就可以$O(nc)$做了。
那么我们可以考虑预处理出$g(i,j,k)$表示两个非空白列i,j中间的k个空白列对答案的贡献。
不过其实不需要记录$(i,j)$,我们可以按照这四个颜色互相是否相同分出5种等价类,大概是
- $\left[ \begin{matrix} x & \cdots & x\\ y & \cdots & y \end{matrix} \right]$
- $\left[ \begin{matrix} x & \cdots & y\\ y & \cdots & x \end{matrix} \right]$
- $\left[ \begin{matrix} x & \cdots & z\\ y & \cdots & y \end{matrix} \right]$或$\left[ \begin{matrix} x & \cdots & x\\ y & \cdots & z \end{matrix} \right]$
- $\left[ \begin{matrix} x & \cdots & z\\ y & \cdots & x \end{matrix} \right]$或$\left[ \begin{matrix} x & \cdots & y\\ y & \cdots & z \end{matrix} \right]$
- $\left[ \begin{matrix} x & \cdots & z\\ y & \cdots & w \end{matrix} \right]$
显然每个等价类里的$(i,j,k)$算出来是相等的,那么只需要维护$g(5,k)$了,推一下转移式子即可。
注意推出来的式子里z,w是不固定的,而原dp转移时这四个数都是固定的,所以还要除一下。
然后回到原dp,对于两个非空白列我们再分类讨论一下有没有无空白列,枚举当前这一列填的数转移即可(之前那列填的数直接整体转移)。
这样可以获得96分的好成绩,然后发现这个dp的转移跟这场比赛的第一题是一样的(伏笔消除),于是复制粘贴再改改就可以100了。
虽然这么说……100也只有石神这种代码能力很强的强者能写出来,我是真的不行了(
套路:
- 子问题之间互相影响$\rightarrow$一般只有dp做法。
- 有大量具有相同性质的元素$\rightarrow$合并计算以降低复杂度。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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\n",ans); return 0; }