zoukankan      html  css  js  c++  java
  • [BZOJ]1079 着色方案(SCOI2008)

      相邻色块不同的着色方案,似乎这道题已经见过3个版本了。

    Description

      有n个木块排成一行,从左到右依次编号为1~n。你有k种颜色的油漆,其中第i种颜色的油漆足够涂ci个木块。所有油漆刚好足够涂满所有木块,即c1+c2+...+ck=n。相邻两个木块涂相同色显得很难看,所以你希望统计任意两个相邻木块颜色不同的着色方案。

    Input

      第一行为一个正整数k,第二行包含k个整数c1, c2, ... , ck。

    Output

      输出一个整数,即方案总数模1,000,000,007的结果。

    Sample Input

      3
      1 2 3

    Sample Output

      10

    HINT

      1 <= k <= 15,,1 <= ci <= 5。

    Solution

      k和ci都这么小,状压肯定没跑了。

      如果你把k和ci的数据范围换一换,你应该会很容易地设计出状态吧。

      我们先试着从5^15的状态表示法入手,看看有什么可改进的地方。

      你会发现有很多状态本质上是一样的,颜色之间其实是没有区别的。

      例如七种颜色的数量{1,4,3,2,2,1,2}和{1,2,3,2,4,2,1}排序后都是{1,1,2,2,2,3,4}。

      所以我们就试着把状态压一压,状态表示为当前每种数量的颜色有多少种。

      这样就状态又变成15^5啦,科学得不要不要的。

      具体状态为f[i][j][k]表示已经涂了i格,状态为j,最后一格涂的是在当前状态中数量为k的颜色,转移自己看着办吧。

      时间复杂度O(n*k^ci*ci^2),记忆化搜索似乎会省掉那个n?(n=Σci)

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define MN 1100005
    #define mod 1000000007
    using namespace std;
    int m,n,S;
    int g[6],ys[6],f[2][MN][6],q[2][MN],tp[2];
    bool u[MN];
    
    inline int read()
    {
        int n=0,f=1; char c=getchar();
        while (c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
        while (c>='0' && c<='9') {n=n*10+c-'0'; c=getchar();}
        return n*f;
    }
    
    inline void rw(int &x,int y) {x+=y; if (x>=mod) x-=mod;}
    
    int main()
    {
        register int x,i,j,k,l,lg,rg,gs;
        for (m=read();m--;) ++g[x=read()],n+=x;
        for (ys[1]=1,i=2;i<=5;++i) ys[i]=ys[i-1]<<4;
        for (i=1;i<=5;++i) S+=g[i]*ys[i];
        for (f[0][q[0][tp[0]=1]=S][0]=1,lg=0,rg=1,i=0;i<n;++i,swap(lg,rg))
        {
            tp[rg]=0;    
            for (j=1;j<=tp[lg];++j) if (!u[q[lg][j]])
                for (u[q[lg][j]]=true,k=0;k<5;f[lg][q[lg][j]][k++]=0) if (f[lg][q[lg][j]][k])
                    for (x=q[lg][j],l=5;l;x%=ys[l--]) if (gs=x/ys[l])
                        if (k!=l)
                            rw(f[rg][q[lg][j]-ys[l]+ys[l-1]][l-1],1LL*f[lg][q[lg][j]][k]*gs%mod),
                            q[rg][++tp[rg]]=q[lg][j]-ys[l]+ys[l-1];
                        else if (gs>1)
                            rw(f[rg][q[lg][j]-ys[l]+ys[l-1]][l-1],1LL*f[lg][q[lg][j]][k]*(gs-1)%mod),
                            q[rg][++tp[rg]]=q[lg][j]-ys[l]+ys[l-1];
            for (j=1;j<=tp[lg];++j) u[q[lg][j]]=false;
        }
        printf("%d",f[lg][0][0]);
    }

    Last Word

      世界上还有比恶意散播题解的更毒的人吗?

  • 相关阅读:
    反射之初认识
    面向对象(上)练习一 改进:调用方法
    关于php中id设置自增后不连续的问题
    由于定界符引出的格式错误问题
    PHP 关于timezone问题
    2016.4.29 园子第一天,希望所有的坚持都有所收获
    递归调用
    动手动脑
    界面实验任务
    课程作业02
  • 原文地址:https://www.cnblogs.com/ACMLCZH/p/7639563.html
Copyright © 2011-2022 走看看