zoukankan      html  css  js  c++  java
  • Loj #3111. 「SDOI2019」染色

    Loj #3111. 「SDOI2019」染色

    题目描述

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

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

    输入格式

    第一行有两个整数 (n)(c),分别描述了格点图的大小和总的颜色个数。

    之后两行,每行有 (n) 个整数:如果是 (0) 则表示对应结点未被染色,否则一定是一个 (1)(c) 的整数表示对应结点已经染了某一种颜色。

    输出格式

    输出一个整数,为总的染色方案数对 (10^9 + 9) 取模后的值。

    数据范围与提示

    子任务 (1)(44) 分):(1 ≤ n ≤ 10000)(5 ≤ c ≤ 10000);不存在一列有 (2) 个已染色结点;被染色结点全部位于第一行;第一列和最后一列均有结点已被染色。

    子任务 (2)(32) 分):(1 ≤ n ≤ 10000)(5 ≤ c ≤ 10000);不存在一列有 (2) 个已染色结点;第一列和最后一列均有结点已被染色。

    子任务 (3)(12) 分):(1 ≤ n ≤ 10000)(5 ≤ c ≤ 10000);第一列和最后一列均有结点已被染色。

    子任务 (4)(8) 分):(1 ≤ n ≤ 10000)(5 ≤ c ≤ 10000)

    子任务 (5)(4) 分):(1 ≤ n ≤ 100000)(5 ≤ c ≤ 100000)

    参考博客

    首先我们只需要将有颜色的那些列保留下来就好了,然后只记录另一个格子的颜色,这样状态就是(O(nc))的了。对于一列有两个格子有颜色的情况,我们可以假装该列上第二行没有颜色,然后每次(DP)完了过后将不合法的状态清零就行了。

    然后考虑两个有颜色的列之间怎么转移。我们可以讨论这两列的四个格子之间的关系,然后直接转移,为此我们要预处理出转移数组(具体的就看上面给的那个博客)这样我们就可以批量转移中间的一堆全(0)列了。

    转态的话就有一下几种(中间省略了(0)):

    1. (egin{pmatrix}x&x\y&y end{pmatrix})
    2. (egin{pmatrix}x&y\y&x end{pmatrix})
    3. (egin{pmatrix}x&x\y&w end{pmatrix})
    4. (egin{pmatrix}x&w\y&y end{pmatrix})
    5. (egin{pmatrix}x&w\y&x end{pmatrix})
    6. (egin{pmatrix}x&y\y&w end{pmatrix})
    7. (egin{pmatrix}x&w\y&k end{pmatrix})

    (2,3)是等价的,(4,5)是等价的。转移就不说了。

    这样就有(96)分了。

    96分代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define N 100005
    
    using namespace std;
    inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
    
    const ll mod=1e9+9;
    ll ksm(ll t,ll x) {
    	ll ans=1;
    	for(;x;x>>=1,t=t*t%mod)
    		if(x&1) ans=ans*t%mod;
    	return ans;
    }
    
    ll n,c;
    int a[N],b[N];
    ll g[N][5];
    int pos[N],tot;
    ll f[2][N];
    int col[N];
    int type[N];
    int main() {
    	n=Get(),c=Get();
    	ll trans[5][5]={
    		{0,1,0,c-2,1ll*(c-2)*(c-3)%mod},
    		{1,0,c-2,0,1ll*(c-2)*(c-3)%mod},
    		{0,2,c-2,2*c-5,2ll*(c-3)*(c-3)%mod},
    		{2,0,2*c-5,c-2,2ll*(c-3)*(c-3)%mod},
    		{1,1,c-3,c-3,1ll*(c-3)*(c-4)%mod+1}
    	};
    	
    	g[0][0]=1;
    	for(int i=1;i<=n;i++) {
    		for(int j=0;j<5;j++) {
    			for(int k=0;k<5;k++) {
    				(g[i][k]+=g[i-1][j]*trans[j][k])%=mod;
    			}
    		}
    	}
    	ll inv23=ksm(c-2,mod-2),inv4=ksm((c-2)*(c-3)%mod,mod-2);
    	for(int i=1;i<=n;i++) {
    		g[i][2]=g[i][2]*inv23%mod;
    		g[i][3]=g[i][3]*inv23%mod;
    		g[i][4]=g[i][4]*inv4%mod;
    	}
    	for(int i=1;i<=n;i++) a[i]=Get();
    	for(int i=1;i<=n;i++) b[i]=Get();
    	for(int i=1;i<=n;i++) {
    		if(a[i]&&a[i]==b[i]) {
    			cout<<0;return 0;
    		}
    	}
    	
    	for(int i=1;i<=n;i++) {
    		if(a[i]||b[i]) {
    			if(a[i]&&!b[i]) type[i]=0,col[i]=a[i];
    			if(!a[i]&&b[i]) type[i]=1,col[i]=b[i];
    			if(a[i]&&b[i]) type[i]=2,col[i]=a[i];
    			pos[++tot]=i;
    		}
    	}
    	
    	if(type[pos[1]]!=2) {
    		for(int i=1;i<=c;i++)
    			if(i!=col[pos[1]]) f[1][i]=1;
    	} else f[1][b[pos[1]]]=1;
    	
    	if(pos[1]!=1) {
    		ll tem=(g[pos[1]-1][0]+g[pos[1]-1][1]+2*g[pos[1]-1][2]*(c-2)+2*g[pos[1]-1][3]*(c-2)+g[pos[1]-1][4]*(c-2)%mod*(c-3))%mod;
    		for(int i=1;i<=c;i++) f[1][i]=f[1][i]*tem%mod;
    	}
    	for(int i=2;i<=tot;i++) {
    		int now=i&1;
    		memset(f[now],0,sizeof(f[now]));
    		ll sum=0;
    		for(int j=1;j<=c;j++) sum+=f[now^1][j];
    		sum%=mod;
    		for(int j=1;j<=c;j++) {
    			if(j==col[pos[i]]) continue ;
    			if((type[pos[i-1]]&1)==(type[pos[i]]&1)) {
    				if(col[pos[i-1]]==col[pos[i]]) {
    					f[now][j]=(f[now^1][j]*g[pos[i]-pos[i-1]][0]+(sum-f[now^1][j]+mod)*g[pos[i]-pos[i-1]][2])%mod;
    				} else {
    					if(j==col[pos[i-1]]) {
    						f[now][j]=((sum-f[now^1][col[pos[i]]]+mod)*g[pos[i]-pos[i-1]][3]+f[now^1][col[pos[i]]]*g[pos[i]-pos[i-1]][1])%mod;
    					} else {
    						f[now][j]=(f[now^1][j]*g[pos[i]-pos[i-1]][2]+f[now^1][col[pos[i]]]*g[pos[i]-pos[i-1]][3]+(sum-f[now^1][j]-f[now^1][col[pos[i]]]+2*mod)*g[pos[i]-pos[i-1]][4])%mod;
    					}
    				}
    			} else {
    				if(col[pos[i-1]]==col[pos[i]]) {
    					f[now][j]=(f[now^1][j]*g[pos[i]-pos[i-1]][1]+(sum-f[now^1][j]+mod)*g[pos[i]-pos[i-1]][3])%mod;
    				} else {
    					if(j==col[pos[i-1]]) {
    						f[now][j]=((sum-f[now^1][col[pos[i]]]+mod)*g[pos[i]-pos[i-1]][2]+f[now^1][col[pos[i]]]*g[pos[i]-pos[i-1]][0])%mod;
    					} else {
    						f[now][j]=(f[now^1][j]*g[pos[i]-pos[i-1]][3]+f[now^1][col[pos[i]]]*g[pos[i]-pos[i-1]][2]+(sum-f[now^1][j]-f[now^1][col[pos[i]]]+2*mod)*g[pos[i]-pos[i-1]][4])%mod;
    					}
    				}
    			}
    		}
    		if(type[pos[i]]==2) for(int j=1;j<=c;j++) if(j!=b[pos[i]]) f[now][j]=0;
    	}
    	ll sum=0;
    	for(int i=1;i<=c;i++) sum+=f[tot&1][i];
    	sum%=mod;
    	if(pos[tot]<n) {
    		sum=sum*((g[n-pos[tot]][0]+g[n-pos[tot]][1]+2*g[n-pos[tot]][2]*(c-2)+2*g[n-pos[tot]][3]*(c-2)+g[n-pos[tot]][4]*(c-2)%mod*(c-3))%mod)%mod;
    	}
    	cout<<sum;
    	return 0;
    }
    
    
    

    优化的话可以发现每次(DP)只有几个状态的转移和其他的不一样,所以开颗线段树维护下就好了,当然也可以用(SDOI2019 D1 T1)的方法(还有这种玩法??)

    (100)分代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define N 100005
    
    using namespace std;
    inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
    
    const ll mod=1e9+9;
    ll ksm(ll t,ll x) {
    	ll ans=1;
    	for(;x;x>>=1,t=t*t%mod)
    		if(x&1) ans=ans*t%mod;
    	return ans;
    }
    
    ll n,c;
    int a[N],b[N];
    ll g[N][5];
    int pos[N],tot;
    ll f[2][N];
    int col[N];
    int type[N];
    struct tree {
    	int l,r;
    	ll sum;
    	ll mul,add;
    }tr[N<<2];
    
    void update(int v) {
    	tr[v].sum=tr[v<<1].sum+tr[v<<1|1].sum;
    	if(tr[v].sum>=mod) tr[v].sum-=mod;
    }
    
    void Add(int v,ll f) {
    	(tr[v].sum+=f*(tr[v].r-tr[v].l+1))%=mod;
    	(tr[v].add+=f)%=mod;
    }
    
    void Mul(int v,ll f) {
    	tr[v].sum=tr[v].sum*f%mod;
    	tr[v].add=tr[v].add*f%mod;
    	tr[v].mul=tr[v].mul*f%mod;
    }
    
    void down(int v) {
    	if(tr[v].mul!=1) {
    		Mul(v<<1,tr[v].mul);
    		Mul(v<<1|1,tr[v].mul);
    		tr[v].mul=1;
    	}
    	if(tr[v].add) {
    		Add(v<<1,tr[v].add);
    		Add(v<<1|1,tr[v].add);
    		tr[v].add=0;
    	}
    }
    
    void build(int v,int l,int r) {
    	tr[v].l=l,tr[v].r=r;
    	tr[v].mul=1,tr[v].add=0;
    	if(l==r) {
    		tr[v].sum=f[1][l];
    		return ;
    	}
    	int mid=l+r>>1;
    	build(v<<1,l,mid),build(v<<1|1,mid+1,r);
    	update(v);
    }
    
    void Add(int v,int l,int r,ll f) {
    	if(tr[v].l>r||tr[v].r<l) return ;
    	if(l<=tr[v].l&&tr[v].r<=r) {
    		Add(v,f);
    		return ;
    	}
    	down(v);
    	Add(v<<1,l,r,f),Add(v<<1|1,l,r,f);
    	update(v);
    }
    
    void Mul(int v,int l,int r,ll f) {
    	if(tr[v].l>r||tr[v].r<l) return ;
    	if(l<=tr[v].l&&tr[v].r<=r) {
    		Mul(v,f);
    		return ;
    	}
    	down(v);
    	Mul(v<<1,l,r,f),Mul(v<<1|1,l,r,f);
    	update(v);
    }
    
    ll query(int v,int l,int r) {
    	if(tr[v].l>r||tr[v].r<l) return 0;
    	if(l<=tr[v].l&&tr[v].r<=r) return tr[v].sum;
    	down(v);
    	return (query(v<<1,l,r)+query(v<<1|1,l,r))%mod;
    }
    
    int main() {
    	n=Get(),c=Get();
    	ll trans[5][5]={
    		{0,1,0,c-2,1ll*(c-2)*(c-3)%mod},
    		{1,0,c-2,0,1ll*(c-2)*(c-3)%mod},
    		{0,2,c-2,2*c-5,2ll*(c-3)*(c-3)%mod},
    		{2,0,2*c-5,c-2,2ll*(c-3)*(c-3)%mod},
    		{1,1,c-3,c-3,1ll*(c-3)*(c-4)%mod+1}
    	};
    	g[0][0]=1;
    	for(int i=1;i<=n;i++) {
    		for(int j=0;j<5;j++) {
    			for(int k=0;k<5;k++) {
    				(g[i][k]+=g[i-1][j]*trans[j][k])%=mod;
    			}
    		}
    	}
    	ll inv23=ksm(c-2,mod-2),inv4=ksm((c-2)*(c-3)%mod,mod-2);
    	for(int i=1;i<=n;i++) {
    		g[i][2]=g[i][2]*inv23%mod;
    		g[i][3]=g[i][3]*inv23%mod;
    		g[i][4]=g[i][4]*inv4%mod;
    	}
    	for(int i=1;i<=n;i++) a[i]=Get();
    	for(int i=1;i<=n;i++) b[i]=Get();
    	for(int i=1;i<=n;i++) {
    		if(a[i]&&a[i]==b[i]) {
    			cout<<0;return 0;
    		}
    	}
    	for(int i=1;i<=n;i++) {
    		if(a[i]||b[i]) {
    			if(a[i]&&!b[i]) type[i]=0,col[i]=a[i];
    			if(!a[i]&&b[i]) type[i]=1,col[i]=b[i];
    			if(a[i]&&b[i]) type[i]=2,col[i]=a[i];
    			pos[++tot]=i;
    		}
    	}
    	if(type[pos[1]]!=2) {
    		for(int i=1;i<=c;i++)
    			if(i!=col[pos[1]]) f[1][i]=1;
    	} else f[1][b[pos[1]]]=1;
    	
    	if(pos[1]!=1) {
    		ll tem=(g[pos[1]-1][0]+g[pos[1]-1][1]+2*g[pos[1]-1][2]*(c-2)+2*g[pos[1]-1][3]*(c-2)+g[pos[1]-1][4]*(c-2)%mod*(c-3))%mod;
    		for(int i=1;i<=c;i++) f[1][i]=f[1][i]*tem%mod;
    	}
    	build(1,1,c);
    	for(int i=2;i<=tot;i++) {
    		ll sum=query(1,1,c);
    		if((type[pos[i-1]]&1)==(type[pos[i]]&1)) {
    			if(col[pos[i-1]]==col[pos[i]]) {
    				Mul(1,1,c,g[pos[i]-pos[i-1]][0]-g[pos[i]-pos[i-1]][2]+mod);
    				Add(1,1,c,sum*g[pos[i]-pos[i-1]][2]%mod);
    			} else {
    				ll x=query(1,col[pos[i]],col[pos[i]]);
    				Mul(1,col[pos[i-1]],col[pos[i-1]]-1,0);
    				Add(1,col[pos[i-1]],col[pos[i-1]],((sum-x+mod)*g[pos[i]-pos[i-1]][3]+x*g[pos[i]-pos[i-1]][1])%mod);
    				
    				Mul(1,1,col[pos[i-1]]-1,g[pos[i]-pos[i-1]][2]-g[pos[i]-pos[i-1]][4]+mod);
    				Mul(1,col[pos[i-1]]+1,c,g[pos[i]-pos[i-1]][2]-g[pos[i]-pos[i-1]][4]+mod);
    				Add(1,1,col[pos[i-1]]-1,(x*g[pos[i]-pos[i-1]][3]+(sum-x+mod)*g[pos[i]-pos[i-1]][4])%mod);
    				Add(1,col[pos[i-1]]+1,c,(x*g[pos[i]-pos[i-1]][3]+(sum-x+mod)*g[pos[i]-pos[i-1]][4])%mod);
    			}
    		} else {
    			if(col[pos[i-1]]==col[pos[i]]) {
    				Mul(1,1,c,g[pos[i]-pos[i-1]][1]-g[pos[i]-pos[i-1]][3]+mod);
    				Add(1,1,c,sum*g[pos[i]-pos[i-1]][3]%mod);
    			} else {
    				ll x=query(1,col[pos[i]],col[pos[i]]);
    				Mul(1,col[pos[i-1]],col[pos[i-1]]-1,0);
    				Add(1,col[pos[i-1]],col[pos[i-1]],((sum-x+mod)*g[pos[i]-pos[i-1]][2]+x*g[pos[i]-pos[i-1]][0])%mod);
    				
    				Mul(1,1,col[pos[i-1]]-1,g[pos[i]-pos[i-1]][3]-g[pos[i]-pos[i-1]][4]+mod);
    				Mul(1,col[pos[i-1]]+1,c,g[pos[i]-pos[i-1]][3]-g[pos[i]-pos[i-1]][4]+mod);
    				Add(1,1,col[pos[i-1]]-1,(x*g[pos[i]-pos[i-1]][2]+(sum-x+mod)*g[pos[i]-pos[i-1]][4])%mod);
    				Add(1,col[pos[i-1]]+1,c,(x*g[pos[i]-pos[i-1]][2]+(sum-x+mod)*g[pos[i]-pos[i-1]][4])%mod);
    			}
    		}
    		Mul(1,col[pos[i]],col[pos[i]],0);
    		if(type[pos[i]]==2) {
    			if(b[pos[i]]>1) Mul(1,1,b[pos[i]]-1,0);
    			if(b[pos[i]]<n) Mul(1,b[pos[i]]+1,c,0);
    		}
    	}
    	ll sum=0;
    	sum=query(1,1,c);
    	sum%=mod;
    	if(pos[tot]<n) sum=sum*((g[n-pos[tot]][0]+g[n-pos[tot]][1]+2*g[n-pos[tot]][2]*(c-2)+2*g[n-pos[tot]][3]*(c-2)+g[n-pos[tot]][4]*(c-2)%mod*(c-3))%mod)%mod;
    	cout<<sum;
    	return 0;
    }
    
    
  • 相关阅读:
    12个思维管理工具:标杆分析法、麦肯锡7步分析法、SMART原则....
    Capturing Audio in Android Q
    闭环思维
    如何下载Google Play商店的apk?如何安装分割的apk ?
    强制删除文件 或 文件夹
    ToDesk ---- 个人免费 极致流畅的远程协助软件
    无法显示内挂(非内嵌)字幕-
    MKVToolNix 一款Matroska(.mkv)格式编辑工具,可以将超过16条音轨/字幕封装到一个.mkv文件中去
    Symantec Endpoint Protection(赛门铁克杀毒软件) 如何添加白名单避免被误删、误杀?
    二维码识别工具
  • 原文地址:https://www.cnblogs.com/hchhch233/p/10874098.html
Copyright © 2011-2022 走看看