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

发布时间 2023-12-21 20:13:30作者: Farmer_D

题目链接

点击打开链接

题目解法

题意比较复杂,形式化一下题意是:
一些人和一些城市,每个人属于一个城市,每个人属于 \(A/B/C/D\) 队,需要满足:每个城市中的人要么都属于 \(AC\)\(BD\),且 \(A+C\le C_0,\;B+D\le C_1,\;A+B\le D_0,\;C+D\le D_1\)

暴力 \(dp\)\(f_{i,a,b,c}\) 表示到第 \(i\) 个城市,\(A,B,C\) 集合中有几个人的方案数,是 \(O(n^5)\)
这样显然是没有必要的,因为题目只限制了两数之和。所以令 \(f_{i,j,k}\) 为到第 \(i\) 个城市,\(AB\) 集合中一共有 \(j\) 个人,\(AC\) 集合中一共有 \(k\) 个人的方案数,不难 \(O(n^4)\) 转移

首先考虑 \(k=0\) 的情况
一个发现是:每个人属于 \(AC\)\(BD\) 集合 与 属于 \(AB\)\(CD\) 集合的方案数是独立的
即每个人可以先选择加入 \(AC\)\(BD\),再选择 \(AB\)\(CD\),贡献是独立的,可以分别计算然后累乘
这不难 \(dp\) 求解

考虑推广到 \(k>0\) 的情况
观察到 \(k\le 30\),考虑对于每个有限制的人的城市,单独计算
没有限制的城市直接按照 \(k=0\) 的处理方式即可
发现对于 \(AB\)\(CD\) 一维,人与城市是独立的,所以可以把 城市有限制 但 人没有限制 的在 \(k=0\)\(dp\) 中一并求解
然后就可以用暴力 \(dp\) 的方法求解,可以发现 \(j\le M(2500),\le k\le 300(10k)\),所以暴力做是 ok 的

时间复杂度 \(O(nm+10k^2m)\)

#include <bits/stdc++.h>
#define F(i,x,y) for(int i=(x);i<=(y);i++)
#define DF(i,x,y) for(int i=(x);i>=(y);i--)
#define ms(x,y) memset(x,y,sizeof(x))
#define SZ(x) (int)x.size()-1
#define pb push_back
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
typedef pair<int,int> pii;
template<typename T> void chkmax(T &x,T y){ x=max(x,y);}
template<typename T> void chkmin(T &x,T y){ x=min(x,y);}
inline int read(){
    int FF=0,RR=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    return FF*RR;
}
const int N=1100,P=998244353,K=32,M=2510;
int n,c,C0,C1,D0,D1,k;
int f[M],g[M],h[M][K*10],tmp[M][K*10];
int b[N],s[N],hate[N];
vector<pii> city[N];
int cnt[4];
inline void inc(int &x,int y){ x+=y;if(x>=P) x-=P;}
int calc(int *a,int l,int r){
    if(l>r||r<0) return 0;
    l=max(l,0);
    if(!l) return a[r];
    else return (a[r]-a[l-1]+P)%P;
}
void work(){
    n=read(),c=read(),C0=read(),C1=read(),D0=read(),D1=read();
    int tot=0;
    F(i,1,n) b[i]=read(),s[i]=read(),tot+=s[i];
    k=read();
    ms(hate,-1);
    F(i,1,k){ int x=read();hate[x]=read();if(hate[x]==1||hate[x]==2) hate[x]=3-hate[x];}
    F(i,1,n) city[b[i]].pb({s[i],hate[i]});
    ms(g,0),ms(f,0),ms(h,0);
    int bound1=0,bound2=0,bound3=0,bound4=0;
    g[0]=f[0]=h[0][0]=1;
    F(i,1,n){
        if(city[i].empty()) continue;
        int addsz=0;
        bool flg=false;
        for(auto [siz,hate]:city[i]){
            addsz+=siz;
            if(hate!=-1) flg=true,bound4+=siz;
            else{ bound1=min(bound1+siz,M-1);DF(j,bound1,siz) inc(g[j],g[j-siz]);}
        }
        if(!flg){ bound2=min(bound2+addsz,M-1);DF(j,bound2,addsz) inc(f[j],f[j-addsz]);continue;}
        bound3=min(bound3+addsz,M-1),chkmin(bound4,M-1);
        F(j,0,bound3) F(k,0,bound4) tmp[j][k]=h[j][k];
        for(auto [siz,hate]:city[i]){
            if(hate==2) DF(j,bound3,siz) DF(k,bound4,siz) h[j][k]=h[j-siz][k-siz],h[j-siz][k-siz]=0;
            else if(hate==0||hate==-1) DF(j,bound3,siz) DF(k,bound4,0) h[j][k]=h[j-siz][k],h[j-siz][k]=0;
            else DF(j,bound3,siz) DF(k,bound4,0){
                    h[j][k]=h[j-siz][k],h[j-siz][k]=0;
                    if(k>=siz) inc(h[j][k],h[j-siz][k-siz]);
                }
        }
        for(auto [siz,hate]:city[i]){
            if(hate==1||hate==-1) ;
            else if(hate==3) DF(j,bound3,0) DF(k,bound4,siz) tmp[j][k]=tmp[j][k-siz],tmp[j][k-siz]=0;
            else DF(j,bound3,0) DF(k,bound4,siz) inc(tmp[j][k],tmp[j][k-siz]);
        }
        F(j,0,bound3) F(k,0,bound4) inc(h[j][k],tmp[j][k]);
    }
    F(i,1,M-1) inc(f[i],f[i-1]),inc(g[i],g[i-1]);
    int ans=0;
    F(i,0,M-1) F(j,0,K*10-1) if(h[i][j]) inc(ans,1ll*h[i][j]*calc(f,tot-i-C1,C0-i)%P*calc(g,tot-j-D1,D0-j)%P);
    printf("%d\n",ans);
    F(i,1,n) city[i].clear();
}
int main(){
    int T=read();
    while(T--) work();
    return 0;
}