Luogu5289 [十二省联考2019]皮配
Luogu5289 [十二省联考2019]皮配
背包
对于没有偏好的学校,可以发现城市选阵营、学校选派系是没有关系的。因此我们可以用两次背包求出两类答案再进行合并。
考虑对于有限制的学校单独\(dp\)。
\(F_{i,j}\)表示当前城市在蓝阵营,蓝阵营中有\(i\)人(不包括当前城市),且有\(j\)个人在鸭派系的方案数。
\(G_{i,j}\)表示当前城市在红阵营,蓝阵营中有\(i\)人(不包括当前城市),且有\(j\)个人在鸭派系的方案数。
\(dp_{i,j,k}\)表示前\(i\)个有限制的学校,有\(j\)人在蓝阵营,\(k\)人在鸭派系的方案数。
然后两个背包合并,预处理前缀和。
如果一个学校没有偏好,但是该城市存在学校有偏好,依然不影响答案,因为这时候学校只负责选择派系,阵营会由\(dp\)数组计算,没有偏好的学校的阵营直接与城市一致即可。
\(Code:\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 1005
#define M 2505
#define ll long long
using namespace std;
const int p=998244353;
struct modint
{
int x;
modint (int xx=0)
{
x=xx;
}
modint operator + (modint A)
{
return modint((x+A.x)%p);
}
modint operator + (int A)
{
return modint((x+A)%p);
}
modint operator - (modint A)
{
return modint((x-A.x)%p);
}
modint operator - (int A)
{
return modint((x-A)%p);
}
modint operator * (modint A)
{
return modint((ll)x*A.x%p);
}
modint operator * (int A)
{
return modint((ll)x*A%p);
}
void operator += (modint A)
{
(*this)=(*this)+A;
}
void operator *= (modint A)
{
(*this)=(*this)*A;
}
void operator += (int A)
{
(*this)=(*this)+A;
}
void operator *= (int A)
{
(*this)=(*this)*A;
}
void setNum(int t)
{
x=t;
}
void read()
{
scanf("%d",&x);
}
void print()
{
x=(x%p+p)%p;
printf("%d\n",x);
}
}f[M],g[M],F[M][M],G[M][M],dp[M][M];
int T,n,c,k,x,y,c0,c1,d0,d1,mx,my,s0;
int bel[N],s[N],cit[N];
int hate[N];
bool hat[N];
modint calc(int a,int b)
{
int L1=max(s0-c1-a,0),R1=c0-a,L2=max(s0-d1-b,0),R2=d0-b;
if (L1>R1 || L2>R2)
return 0;
return (f[R1]-f[L1-1])*(g[R2]-g[L2-1]);
}
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&c);
memset(cit,0,(c+1)*sizeof(int)),s0=0;
scanf("%d%d%d%d",&c0,&c1,&d0,&d1);
for (int i=1;i<=n;++i)
scanf("%d%d",&bel[i],&s[i]),cit[bel[i]]+=s[i],s0+=s[i];
memset(hate,-1,(n+1)*sizeof(int));
memset(hat,false,(c+1)*sizeof(bool));
scanf("%d",&k);
for (int i=1;i<=k;++i)
{
scanf("%d%d",&x,&y);
hate[x]=y,hat[bel[x]]=true;
}
memset(f,0,(c0+1)*sizeof(modint));
f[0]=1;
for (int i=1;i<=c;++i)
{
if (!cit[i] || hat[i])
continue;
for (int j=c0;j>=cit[i];--j)
f[j]+=f[j-cit[i]];
}
memset(g,0,(d0+1)*sizeof(modint));
g[0]=1;
for (int i=1;i<=n;++i)
{
if (~hate[i])
continue;
for (int j=d0;j>=s[i];--j)
g[j]+=g[j-s[i]];
}
for (int i=0;i<=c0;++i)
for (int j=0;j<=d0;++j)
dp[i][j]=F[i][j]=G[i][j]=0;
mx=my=0;
dp[0][0]=1;
for (int i=1;i<=c;++i)
{
if (!hat[i])
continue;
for (int j=0;j<=mx;++j)
for (int k=0;k<=my;++k)
F[j][k]=G[j][k]=dp[j][k];
for (int j=1;j<=n;++j)
{
if (!~hate[j] || bel[j]!=i)
continue;
my=min(d0,my+s[j]);
for (int A=mx;A>=0;--A)
for (int B=my;B>=0;--B)
F[A][B]=((B>=s[j] && hate[j]!=0)?F[A][B-s[j]]:0)+((hate[j]!=1)?F[A][B]:0),
G[A][B]=((B>=s[j] && hate[j]!=2)?G[A][B-s[j]]:0)+((hate[j]!=3)?G[A][B]:0);
}
mx=min(c0,mx+cit[i]);
for (int j=0;j<=mx;++j)
for (int k=0;k<=my;++k)
dp[j][k]=((j>=cit[i])?F[j-cit[i]][k]:0)+G[j][k];
}
for (int i=1;i<=c0;++i)
f[i]+=f[i-1];
for (int i=1;i<=d0;++i)
g[i]+=g[i-1];
modint ans;
for (int i=0;i<=mx;++i)
for (int j=0;j<=my;++j)
ans+=dp[i][j]*calc(i,j);
ans.print();
}
return 0;
}