Loj #3111. 「SDOI2019」染色

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;
}

原文地址:https://www.cnblogs.com/hchhch233/p/10874098.html

时间: 2024-10-12 06:59:12

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

loj#2552. 「CTSC2018」假面

题目链接 loj#2552. 「CTSC2018」假面 题解 本题严谨的证明了我菜的本质 对于砍人的操作好做找龙哥就好了,blood很少,每次暴力维护一下 对于操作1 设\(a_i\)为第i个人存活的概率,\(d_i\)为死掉的概率,\(g_{i,j}\)是除i以外活了j个人的概率 那个选中i人的答案就是 \[a_i\times\sum_{j = 0} ^{k - 1}\frac{g_{i,j}}{j + 1}\] 对于\(g_{i,j}\) ,设\(f_{i,j}\)表示前\(i\)个人有\(

loj#2076. 「JSOI2016」炸弹攻击 模拟退火

目录 题目链接 题解 代码 题目链接 loj#2076. 「JSOI2016」炸弹攻击 题解 模拟退火 退火时,由于答案比较小,但是温度比较高 所以在算exp时最好把相差的点数乘以一个常数让选取更差的的概率降低 代码 #include<ctime> #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> #define gc getchar() #define

Loj #2541「PKUWC2018」猎人杀

Loj #2541. 「PKUWC2018」猎人杀 题目链接 好巧妙的题! 游戏过程中,概率的分母一直在变化,所以就非常的不可做. 所以我们将问题转化一下:我们可以重复选择相同的猎人,只不过在一个猎人被选择了过后我们就给他打上标记,再次选择他的时候就无效.这样与原问题是等价的. 证明: 设\(sum=\sum_iw_i,kill=\sum_{i被杀死了}w_i\). 攻击到未被杀死的猎人\(i\)的概率为\(P\). 则根据题意\(P=\frac{w_i}{sum-kill}\). 问题转化后:

Loj #2542. 「PKUWC2018」随机游走

Loj #2542. 「PKUWC2018」随机游走 题目描述 给定一棵 \(n\) 个结点的树,你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 \(Q\) 次询问,每次询问给定一个集合 \(S\),求如果从 \(x\) 出发一直随机游走,直到点集 \(S\) 中所有点都至少经过一次的话,期望游走几步. 特别地,点 \(x\)(即起点)视为一开始就被经过了一次. 答案对 $998244353 $ 取模. 输入格式 第一行三个正整数 \(n,Q,x\). 接下来 \(

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

Loj #2192. 「SHOI2014」概率充电器 题目描述 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品--概率充电器: 「采用全新纳米级加工技术,实现元件与导线能否通电完全由真随机数决定!SHOI 概率充电器,您生 活不可或缺的必需品!能充上电吗?现在就试试看吧!」 SHOI 概率充电器由 \(n-1\) 条导线连通了 \(n\) 个充电元件.进行充电时,每条导线是否可以导电以 概率决定,每一个充电元件自身是否直接进行充电也由概率决定.随后电能可以从直接充电的元件经

Loj #2553. 「CTSC2018」暴力写挂

Loj #2553. 「CTSC2018」暴力写挂 题目描述 temporaryDO 是一个很菜的 OIer .在 4 月,他在省队选拔赛的考场上见到了<林克卡特树>一题,其中 \(k = 0\) 的部分分是求树 \(T\) 上的最长链.可怜的 temporaryDO 并不会做这道题,他在考场上抓猫耳挠猫腮都想不出一点思路. 这时,善良的板板出现在了空中,他的身上发出璀璨却柔和的光芒,荡漾在考场上.''题目并不难.'' 板板说.那充满磁性的声音,让 temporaryDO 全身充满了力量. 他

Loj #2554. 「CTSC2018」青蕈领主

Loj #2554. 「CTSC2018」青蕈领主 题目描述 "也许,我的生命也已经如同风中残烛了吧."小绿如是说. 小绿同学因为微积分这门课,对"连续"这一概念产生了浓厚的兴趣.小绿打算把连续的概念放到由整数构成的序列上,他定义一个长度为 \(m\) 的整数序列是连续的,当且仅当这个序列中的最大值与最小值的差,不超过\(m-1\).例如 \(\{1,3,2\}\) 是连续的,而 \(\{1,3\}\) 不是连续的. 某天,小绿的顶头上司板老大,给了小绿 \(T\)

LOJ #2037. 「SHOI2015」脑洞治疗仪

#2037. 「SHOI2015」脑洞治疗仪 题目描述 曾经发明了自动刷题机的发明家 SHTSC 又公开了他的新发明:脑洞治疗仪——一种可以治疗他因为发明而日益增大的脑洞的神秘装置. 为了简单起见,我们将大脑视作一个 01 序列.1 代表这个位置的脑组织正常工作,0 代表这是一块脑洞. 1 0 1 0 0 0 1 1 1 0 脑洞治疗仪修补某一块脑洞的基本工作原理就是将另一块连续区域挖出,将其中正常工作的脑组织填补在这块脑洞中.(所以脑洞治疗仪是脑洞的治疗仪?) 例如,用上面第 8 号位置到第 

LOJ #2141. 「SHOI2017」期末考试

题目链接 LOJ #2141 题解 据说这道题可以三分(甚至二分)? 反正我是枚举的 = = 先将t和b数组排序后计算出前缀和, 然后枚举最晚的出成绩时间,每次可以O(1)直接计算调整到该时间所需的代价. 如何计算? 对于学生不满意造成的代价,是 (不满意人数 * 最晚结束时间) - 所有不满的人的t之和; 对于调整老师造成的代价, A < B 时先用A调整 (可用前缀和计算出有多少时间能用来交换,又有多少时间需要被交换)再用B调整仍超出的部分; 否则都用B调整. 真的如高大佬所言是sb题啊 =