HDU 6578 Blank

hdu题面

Time limit 1000 ms

Memory limit 262144 kB

OS Windows

Source 2019 Multi-University Training Contest 1

感想

火车上AK这场比赛的dls,在两天后直播讲题,讲到这题的时候,说得好轻松啊,一个简单的dp就完事了,然后说了一下dp的参数,然后就到下一题了……

解题思路

扔几个链接,融会贯通一下

大概就是设\(dp[x][y][z][i]\)代表如下的意思:从前向后扫,扫到目前第\(i\)个格子时,4种颜色最后出现的位置分别为\(x、y、z、i\)时的方案数,其中\(x\leqslant y\leqslant z\leqslant i\),在\(x、y、z、i\)为0时取等号,表示目前有一些颜色还没有出现,即最后一次出现的位置为0。

颜色种类不重要,因为计算过程已经把不同颜色的组合方式计算进去了,重要的是颜色的。然后染这一格(\(i\))的时候的方案数是从染上一个格子(\(i-1\))的方案数转移过来的。

扩展当前这一个格子的时候,我们可以取之前出现过的4种颜色中的一种。举个例子,假设我们取的是之前最后一次出现在\(x\)的那个颜色,那么会发生这样的转移——\(dp[y][z][i-1][i]+=dp[x][y][z][i-1]\)(想想dp数组四个参数的含义和大小关系,现在这个状态的数量增加了上一个状态的数量那么多种),取的是最后一次出现在\(y\)的那个颜色的话,发生的转移就是——\(dp[x][z][i-1][i]+=dp[x][y][z][i-1]\),同样的道理还有\(dp[x][y][i-1][i]+=dp[x][y][z][i-1]\)\(dp[x][y][i-1][i]+=dp[x][y][z][i]\)。对于判断条件,当我们跑完第\(i\)个格子的时候,就检查以\(i\)为右端点的所有条件,对于一个条件——\([l,r]\)之间有\(x\)种颜色,我们可以看那些状态是否满足条件,看每个颜色最后一次出现的位置在区间内还是区间外,然后根据这个统计区间内颜色数量,不满足条件就把它们清零,防止转移到下一个\(i\)

统计的时候就统计所有\(i=n\)的格子,把数量加起来就好。但是这种方法会爆空间,注意到\(i\)那一维可以滚动,所以把\(i\)那一维滚动起来。另外注意取模 993244853 998244353。

源代码

#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>

const int mod=998244353;

int T;
int n,m;
long long dp[105][105][105][2];
//x<y<z<i
struct Condition{
    int l,x;
};
std::vector<Condition> c[105];


int main()
{
    //freopen("test.in","r",stdin);
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<105;i++) c[i].clear();
        for(int i=1,l,r,x;i<=m;i++)
        {
            scanf("%d%d%d",&l,&r,&x);
            c[r].push_back({l,x});
        }
        dp[0][0][0][0]=1;
        int cur=1;//i&1
        int last=0;//!(i&1)
        for(int i=1;i<=n;i++)
        {
            for(int z=0;z<=i;z++)
            {
                for(int y=0;y<=z;y++)
                {
                    for(int x=0;x<=y;x++)
                    {
                        dp[x][y][z][cur]=0;//清零
                    }
                }
            }
            for(int z=0;z<i;z++)
            {
                for(int y=0;y<=z;y++)
                {
                    for(int x=0;x<=y;x++)//转移
                    {
                        long long temp=dp[x][y][z][last];
                        dp[x][y][z][cur]+=temp;
                        dp[y][z][i-1][cur]+=temp;
                        dp[x][z][i-1][cur]+=temp;
                        dp[x][y][i-1][cur]+=temp;

                        dp[x][y][z][cur]%=mod;
                        dp[y][z][i-1][cur]%=mod;
                        dp[x][z][i-1][cur]%=mod;
                        dp[x][y][i-1][cur]%=mod;
                    }
                }
            }
            for(unsigned int cc=0;cc<c[i].size();cc++)//判断
            {
                for(int z=0;z<i;z++)
                    for(int y=0;y<=z;y++)
                        for(int x=0;x<=y;x++)
                        {
                            int temp=1+(x>=c[i][cc].l?1:0)+(y>=c[i][cc].l?1:0)+(z>=c[i][cc].l?1:0);//统计区间内颜色数量
                            if(temp!=c[i][cc].x) dp[x][y][z][cur]=0;
                        }
            }
            std::swap(cur,last);
        }
        long long ans=0;
        for(int z=0;z<n;z++)//统计答案
        {
            for(int y=0;y<=z;y++)
            {
                for(int x=0;x<=y;x++)
                {
                    ans+=dp[x][y][z][last];
                    ans%=mod;
                }
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/wawcac-blog/p/11318675.html