P5289 [十二省联考 2019] 皮配 题解

题目传送门

$\large\textbf{Statement.}$

本题的重点在于题面。

有 $n$ 个学校和 $c$ 个城市,每个学校属于一个城市,学校人数给定。

你需要将每个学校和城市染成黑色或白色,其中有 $k$ 个学校不希望自己和所在城市的颜色同时分别为给定的两种颜色,黑/白城市和学校的总人数也有限制。求染色方案数。


$\large\textbf{Solution.}$

令 $dp_{i,j,k}$ 表示考虑了前 $i$ 个有限制的城市,黑色城市和学校的人数和分别为 $j,k$,染色方案数。

对于没有限制的学校,最后一起做一个背包即可。

对于有限制的城市,枚举其颜色,用它的所有有限制的学校更新 $dp$ 值。

最后有限制的和没限制的两个背包合并一下就做完了。

参考代码:

#include<bits/stdc++.h>
#define ll long long
#define mxn 2503
#define md 998244353
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rept(i,a,b) for(int i=a;i<b;++i)
#define drep(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
int T,n,m,k,ans,c[2],d[2],s[mxn],a[mxn],f[mxn],dp[2][mxn][mxn],f1[mxn][mxn],f2[mxn][mxn],d1[mxn],d2[mxn];
bool v[mxn];
vector<int>g[mxn];
signed main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d%d%d%d",&n,&m,&c[0],&c[1],&d[0],&d[1]);
        rep(i,1,m)v[i]=0,g[i].clear();
        rep(i,1,n)scanf("%d%d",&a[i],&s[i]),f[i]=-1,g[a[i]].pb(i);
        scanf("%d",&k);
        for(int i=0,x,y;i<k;++i){
            scanf("%d%d",&x,&y);
            f[x]=y,v[a[x]]=1;
        }
        int sum=0;
        bool fl=0;
        memset(dp,0,sizeof(dp));
        dp[0][0][0]=1;
        rep(i,1,m)if(v[i]){
            int sm=0;
            for(int j:g[i])sm+=s[j];
            fl^=1;
            int s1=sm; 
            drep(j,min(sum+sm,c[0]),0)drep(k,min(sum+sm,d[0]),0){
                dp[fl][j][k]=0;
                f1[j][k]=dp[fl^1][j][k];
            }
            for(int j:g[i])if(f[j]!=-1){
                drep(x,min(sum+sm,c[0]),0)drep(y,min(sum+sm,d[0]),0)f2[x][y]=0;
                drep(x,min(sum,c[0]-s[j]),0){
                    drep(y,min(sum,d[0]),0)if(f1[x][y]){
                        if((f[j]>>1)==1){
                            if(y+s[j]<=d[0])f2[x+s[j]][y+s[j]]=(f2[x+s[j]][y+s[j]]+f1[x][y])%md;
                            f2[x+s[j]][y]=(f2[x+s[j]][y]+f1[x][y])%md;
                        }else if(f[j]&1){
                            if(y+s[j]<=d[0])f2[x+s[j]][y+s[j]]=(f2[x+s[j]][y+s[j]]+f1[x][y])%md;
                        }else f2[x+s[j]][y]=(f2[x+s[j]][y]+f1[x][y])%md;
                    }
                }
                sum+=s[j],sm-=s[j];
                drep(x,min(sum+sm,c[0]),0)drep(y,min(sum+sm,d[0]),0)f1[x][y]=f2[x][y];
            }
            drep(x,min(sum,c[0]-sm),0)drep(y,min(sum,d[0]),0)if(f1[x][y]){
                dp[fl][x+sm][y]=(dp[fl][x+sm][y]+f1[x][y])%md;
            }
            drep(j,min(sum+sm,c[0]),0)drep(k,min(sum+sm,d[0]),0)f1[j][k]=dp[fl^1][j][k];
            sum-=s1-sm,sm=s1;
            for(int j:g[i])if(f[j]!=-1){
                drep(x,min(sum+sm,c[0]),0)drep(y,min(sum+sm,d[0]),0)f2[x][y]=0;
                drep(x,min(sum,c[0]),0){
                    drep(y,min(sum,d[0]),0)if(f1[x][y]){
                        if((f[j]>>1)==0){
                            if(y+s[j]<=d[0])f2[x][y+s[j]]=(f2[x][y+s[j]]+f1[x][y])%md;
                            f2[x][y]=(f2[x][y]+f1[x][y])%md;
                        }else if(f[j]&1){
                            if(y+s[j]<=d[0])f2[x][y+s[j]]=(f2[x][y+s[j]]+f1[x][y])%md;
                        }else f2[x][y]=(f2[x][y]+f1[x][y])%md;
                    }
                }
                sum+=s[j],sm-=s[j];
                drep(x,min(sum+sm,c[0]),0)drep(y,min(sum+sm,d[0]),0)f1[x][y]=f2[x][y];
            }
            drep(x,min(sum,c[0]),0)drep(y,min(sum,d[0]),0)if(f1[x][y]){
                dp[fl][x][y]=(dp[fl][x][y]+f1[x][y])%md;
            }
            sum+=sm;
        }
        memset(d1,0,sizeof(d1));
        memset(d2,0,sizeof(d2));
        d1[0]=d2[0]=1;
        rep(i,1,m)if(!v[i]&&g[i].size()){
            int sm=0;
            for(int j:g[i])sm+=s[j];
            drep(j,c[0],sm)d1[j]=(d1[j]+d1[j-sm])%md;
            for(int j:g[i])drep(k,d[0],s[j])d2[k]=(d2[k]+d2[k-s[j]])%md;
            sum+=sm; 
        }
        rep(i,1,n)if(v[a[i]]&&f[i]==-1){
            drep(k,d[0],s[i])d2[k]=(d2[k]+d2[k-s[i]])%md;
        }
        rep(i,1,c[0])d1[i]=(d1[i]+d1[i-1])%md;
        rep(i,1,d[0])d2[i]=(d2[i]+d2[i-1])%md;
        ans=0;
        rep(i,0,c[0])rep(j,0,d[0]){
            if(c[0]-i<sum-i-c[1]||d[0]-j<sum-j-d[1])continue;
            ans=(ans+(ll)dp[fl][i][j]*(d1[c[0]-i]-(sum-i-c[1]-1>=0?d1[sum-i-c[1]-1]:0))%md*(d2[d[0]-j]-(sum-j-d[1]-1>=0?d2[sum-j-d[1]-1]:0)))%md;
        }
        printf("%d\n",(ans+md)%md); 
    }
    return 0;
}
posted @ 2024-01-07 13:57  zifanwang  阅读(8)  评论(0编辑  收藏  举报  来源