Loj #3111. 「SDOI2019」染色

题目描述

给定 \(2 \times 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. \(\begin{pmatrix}x&x\\y&y \end{pmatrix}\)
  2. \(\begin{pmatrix}x&y\\y&x \end{pmatrix}\)
  3. \(\begin{pmatrix}x&x\\y&w \end{pmatrix}\)
  4. \(\begin{pmatrix}x&w\\y&y \end{pmatrix}\)
  5. \(\begin{pmatrix}x&w\\y&x \end{pmatrix}\)
  6. \(\begin{pmatrix}x&y\\y&w \end{pmatrix}\)
  7. \(\begin{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;
}

Loj #3111. 「SDOI2019」染色的更多相关文章

  1. Loj #2192. 「SHOI2014」概率充电器

    Loj #2192. 「SHOI2014」概率充电器 题目描述 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品--概率充电器: 「采用全新纳米级加工技术,实现元件与导线能否通电完 ...

  2. Loj #3096. 「SNOI2019」数论

    Loj #3096. 「SNOI2019」数论 题目描述 给出正整数 \(P, Q, T\),大小为 \(n\) 的整数集 \(A\) 和大小为 \(m\) 的整数集 \(B\),请你求出: \[ \ ...

  3. Loj #3093. 「BJOI2019」光线

    Loj #3093. 「BJOI2019」光线 题目描述 当一束光打到一层玻璃上时,有一定比例的光会穿过这层玻璃,一定比例的光会被反射回去,剩下的光被玻璃吸收. 设对于任意 \(x\),有 \(x\t ...

  4. Loj #3089. 「BJOI2019」奥术神杖

    Loj #3089. 「BJOI2019」奥术神杖 题目描述 Bezorath 大陆抵抗地灾军团入侵的战争进入了僵持的阶段,世世代代生活在 Bezorath 这片大陆的精灵们开始寻找远古时代诸神遗留的 ...

  5. Loj #2542. 「PKUWC2018」随机游走

    Loj #2542. 「PKUWC2018」随机游走 题目描述 给定一棵 \(n\) 个结点的树,你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 \(Q\) 次询问,每次 ...

  6. Loj #3059. 「HNOI2019」序列

    Loj #3059. 「HNOI2019」序列 给定一个长度为 \(n\) 的序列 \(A_1, \ldots , A_n\),以及 \(m\) 个操作,每个操作将一个 \(A_i\) 修改为 \(k ...

  7. Loj #3056. 「HNOI2019」多边形

    Loj #3056. 「HNOI2019」多边形 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时针方向标号依次为 \(1,2,3, \ldots , n\).最开 ...

  8. Loj #3055. 「HNOI2019」JOJO

    Loj #3055. 「HNOI2019」JOJO JOJO 的奇幻冒险是一部非常火的漫画.漫画中的男主角经常喜欢连续喊很多的「欧拉」或者「木大」. 为了防止字太多挡住漫画内容,现在打算在新的漫画中用 ...

  9. Loj 3058. 「HNOI2019」白兔之舞

    Loj 3058. 「HNOI2019」白兔之舞 题目描述 有一张顶点数为 \((L+1)\times n\) 的有向图.这张图的每个顶点由一个二元组 \((u,v)\) 表示 \((0\le u\l ...

随机推荐

  1. java架构之路-(nginx使用详解)nginx的反向代理和优化配置

    书接上回说,nginx我们学会了简单的配置.那么我今天来聊一下,我们ngxin的一些优化配置(我不是很懂,不敢谈高级配置).我先来看一下nginx的好处和正向代理. nginx的好处 1.可以高并发连 ...

  2. Spring Boot 2.0 快速集成整合消息中间件 Kafka

    欢迎关注个人微信公众号: 小哈学Java, 每日推送 Java 领域干货文章,关注即免费无套路附送 100G 海量学习.面试资源哟!! 个人网站: https://www.exception.site ...

  3. C#通过PInvoke调用c++函数的备忘录

    目前知道的情况被调用的C/C++函数只能是全局函数 不能调用类中的成员方法 被调用的C函数必须使用extern “C“包含,保证采用的导出函数名生成规则和.NET一致 函数调用约定通常使用WINAPI ...

  4. Winform中设置ZedGraph鼠标双击获取距离最近曲线上的点的坐标值

    场景 WInforn中设置ZedGraph的焦点显示坐标格式化以及显示三个坐标数的解决办法: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/det ...

  5. vue中利用Promise封装jsonp并调取数据

    Promise就是一个给一步操作提供的容器,在这个容器里,有两个阶段无法改变的阶段,第一个阶段就是Pending(进行),第二个阶段就是结果阶段,包含Fulfilled(成功).Rejected(失败 ...

  6. ES6的export与Nodejs的module.exports比较

    首先我们要明白一个前提,CommonJS模块规范和ES6模块规范完全是两种不同的概念. CommonJS模块规范 Node应用由模块组成,采用CommonJS模块规范. 根据这个规范,每个文件就是一个 ...

  7. 关于使用Hadoop MR的Eclipse插件开发时遇到Permission denied问题的解决办法【转】

    搭建了一个Hadoop的环境,Hadoop集群环境部署在几个Linux服务器上,现在想使用windows上的Java客户端来操作集群中的HDFS文件,但是在客户端运行时出现了如下的认证错误,被折磨了几 ...

  8. python从入门到放弃之进程锁lock

    # ### lock (互斥锁)"""# 应用在多进程当中# 互斥锁lock : 互斥锁是进程间的get_ticket互相排斥进程之间,谁先抢占到资源,谁就先上锁,等到解 ...

  9. docker的8个使用场景

    1.简化配置 虚拟机的最大好处是能在你的硬件设施上运行各种配置不一样的平台(软件, 系统), Docker在降低额外开销的情况下提供了同样的功能. 它能让你将运行环境和配置放在代码汇总然后部署, 同一 ...

  10. apache配置项

    环境:apache2.24 apache 官方文档:http://httpd.apache.org/docs/2.4/  全部指令索引:   http://httpd.apache.org/docs/ ...