「SDOI2019」染色,LOJ3111,奇妙的转移

正题

      Portal

      妙哉

      我也不知道题解是怎么想出来的。

      先考虑只看有数的两列,会发现,转移只有其中情况:

  1. \begin{pmatrix} x & ... &x \\ y& ... & y \end{pmatrix}
  2. \begin{pmatrix} x & ... &y \\ y& ... & x \end{pmatrix}
  3. \begin{pmatrix} x & ... &z \\ y& ... & y \end{pmatrix}
  4. \begin{pmatrix} x & ... &x \\ y& ... & z \end{pmatrix}
  5. \begin{pmatrix} x & ... &z \\ y& ... & x \end{pmatrix}
  6. \begin{pmatrix} x & ... &y \\ y& ... & z \end{pmatrix}
  7. \begin{pmatrix} x & ... &w \\ y& ... & z \end{pmatrix}

      其中xy不与wz不同。中间就全是0.

      这个东西也是可以Dp出来的,第2和第3种情况是类似的,方案数是一样的,第4和第5种的情况也是类似的。

      然后我们单独提出来就好了。

      用f[i][j]表示第i列,空余那位为j的方案数,空余是什么意思呢?

      首先全0的就不用考虑了,只有一个0的话,空余那位记录的就是那一个0取j的方案数。

      如果没有空余,若为(x,y),认为只有f[i][y]有值就可以了。对于j!=y,f[i][j]=0

      转移具体可以看看代码,主要是分两列数是否全有值来维护。

      线段树优化不怎么懂,在这里暂时留个坑。

#include
using namespace std;

const int N=100010;
int n,C,op,data;
long long dp[2][N],sum[2];
long long g[N][5];
int A[N],B[N];
const long long mod=1e9+9;

long long ksm(long long x,long long t){
	long long tot=1;
	while(t){
		if(t&1) (tot*=x)%=mod;
		(x*=x)%=mod;
		t/=2;
	}
	return tot;
}

void prepare(){
	g[0][1]=g[0][3]=g[0][4]=1;
	long long d=1ll*(C-2)*(C-3)%mod,dd=2*(C-2);
	for(int i=1;i<=n;i++){
		g[i][0]=(g[i-1][1]+g[i-1][3]*dd%mod+g[i-1][4]*d%mod)%mod;
		g[i][1]=(g[i-1][0]+g[i-1][2]*dd%mod+g[i-1][4]*d%mod)%mod;
		g[i][2]=(g[i-1][1]+g[i-1][3]*(dd-1)%mod+g[i-1][2]*(C-2)%mod+g[i-1][4]*(d-C+3+mod)%mod)%mod;
		g[i][3]=(g[i-1][0]+g[i-1][2]*(dd-1)%mod+g[i-1][3]*(C-2)%mod+g[i-1][4]*(d-C+3+mod)%mod)%mod;
		g[i][4]=(g[i-1][0]+g[i-1][1]+g[i-1][2]*2*(C-3)%mod+g[i-1][3]*2*(C-3)%mod+g[i-1][4]*(d-(2*C-7)+mod)%mod)%mod;
		//printf("%lld %lld %lld %lld %lld\n",g[i][0],g[i][1],g[i][2],g[i][3],g[i][4]);
	}
}

int check(int a,int b,int c,int d){
	if(a==c && b==d) return 0;
	if(a==d && b==c) return 1;
	if(a==c || b==d) return 2;
	if(a==d || b==c) return 3;
	return 4;
}

void solve(int x,int y){
	op^=1;memset(dp[op],0,sizeof(dp[op]));
	int a=A[x],b=B[x],c=A[y],d=B[y];
	if(a && b && c && d) dp[op][d]=sum[op^1]*g[y-x-1][check(a,b,c,d)]%mod;
	else if(a && b){
		for(int i=1;i<=C;i++) if(i!=c+d)
			dp[op][i]=sum[op^1]*g[y-x-1][check(a,b,!c?i:c,!d?i:d)]%mod;
	}
	else if(c && d){
		dp[op][d]=((sum[op^1]-dp[op^1][c]-dp[op^1][d])%mod+mod)*g[y-x-1][check(a,b,c,d)]%mod;
		(dp[op][d]+=dp[op^1][c]*g[y-x-1][check(!a?c:a,!b?c:b,c,d)]%mod)%=mod;
		(dp[op][d]+=dp[op^1][d]*g[y-x-1][check(!a?d:a,!b?d:b,c,d)]%mod)%=mod;
	}
	else{
		for(int i=1;i<=C;i++) if(i!=c+d){
			dp[op][i]=(sum[op^1]-dp[op^1][i]-dp[op^1][c+d]+mod+mod)%mod*g[y-x-1][check(a,b,!c?i:c,!d?i:d)]%mod;
			(dp[op][i]+=dp[op^1][i]*g[y-x-1][check(!a?i:a,!b?i:b,!c?i:c,!d?i:d)]%mod)%=mod;
			(dp[op][i]+=dp[op^1][c+d]*g[y-x-1][check(!a?c+d:a,!b?c+d:b,!c?i:c,!d?i:d)]%mod)%=mod;
		}
	}
	sum[op]=0;for(int i=1;i<=C;i++) (sum[op]+=dp[op][i])%=mod;
}

int main(){
	scanf("%d %d",&n,&C);prepare();
	data=(1ll*(C-2)*(C-3)+2*(C-1)-1)%mod;
	for(int i=1;i<=n;i++) scanf("%d",&A[i]);
	for(int i=1;i<=n;i++) scanf("%d",&B[i]);
	int lef=1,rig=n;
	while(!A[lef] && !B[lef]) lef++;
	if(lef==n+1) {printf("%lld\n",ksm(data,n-1)*C%mod*(C-1)%mod);return 0;}
	while(!A[rig] && !B[rig]) rig--;
	if(A[lef] && B[lef])
		dp[op][B[lef]]=sum[op]=1;
	else{
		for(int i=1;i<=C;i++)
			dp[op][i]=(i!=(A[lef]|B[lef]));
		sum[op]=C-1;
	}
	for(int i=lef+1,last=lef;i<=n;i++) if(A[i] || B[i]) solve(last,i),last=i;
	printf("%lld\n",sum[op]*ksm(data,lef-1+n-rig)%mod);
}

     

你可能感兴趣的:(「SDOI2019」染色,LOJ3111,奇妙的转移)