题目大意
题解
首先,可以想到一个\(O(n*m^2)\)的暴力(虽然我考试时没想到)。将学校按所属城市排序,记\(f[x][ty][i][j]\)为前x个学校,前一个学校选择了ty阵营,此时蓝有i个人,鸭派有j个人的方案数。如果\(x\)和\((x-1)\)在同一个城市,那么必须选同阵营。
然后膜题解分析\(k=0\)的情况,发现选阵营和选派系可以分开算!哇smg。。。背包一下然后直接把两次算的方案数乘起来就行了。。
\(k>0\) 的时候,发现有一些学校不能在选某派的同时加入某阵营。但是其他学校选阵营和选派系似乎还是可以分开算。。。于是把未强制的学校按照上一种方法背包一下派系;没有学校被强制的城市也背包一下阵营。。其实就是前两种算法合起来。。。对于有被强制的城市,注意在选择一个阵营以后要把这个城市所有学校全部加入该阵营。。
具体实现见代码。。。
时间复杂度\(O(T*(c*m+n*m+k^2*m\)*10\())\)。
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int Q=2555;
int F[Q],G[Q];
int f[2][2][Q][Q];
struct dt{
int bel,num,dis;
}p1[Q],p2[Q];
bool operator<(dt a,dt b)
{return a.bel<b.bel;}
int bl[Q],val[Q],ops[Q],gg[Q];
const int MOD=998244353;
inline void Add(int &a,int b)
{a+=b;a>=MOD?a-=MOD:1;}
inline int sub(int a,int b)
{a-=b;return a<0?a+MOD:a;}
inline int mul(int a,int b)
{return 1LL*a*b%MOD;}
int rev[Q];
int GG(int l,int r)
{return l>r?0:(l?sub(G[r],G[l-1]):G[r]);}
int GF(int l,int r)
{return l>r?0:(l?sub(F[r],F[l-1]):F[r]);}
void solve()
{
int c0,c1,d0,d1;
int tp1=0,tp2=0;
int n,c;
int sum=0;
scanf("%d%d",&n,&c);
scanf("%d%d%d%d",&c0,&c1,&d0,&d1);
for(int i=1;i<=c;i++)gg[i]=0;
for(int i=1;i<=n;i++){
ops[i]=-1;
scanf("%d%d",&bl[i],&val[i]);
gg[bl[i]]+=val[i];
sum+=val[i];
}
int sss;
for(scanf("%d",&sss);sss;--sss){
int x;
scanf("%d",&x);
scanf("%d",&ops[x]);
}
for(int i=1;i<=n;i++)
(ops[i]>=0?p1[++tp1]:p2[++tp2])=(dt){bl[i],val[i],ops[i]};
sort(p1+1,p1+tp1+1),sort(p2+1,p2+tp2+1);
for(int i=1;i<=tp1;i++){
if(gg[p1[i].bel]<0)rev[i]=0;
else rev[i]=gg[p1[i].bel],gg[p1[i].bel]=-1;
}
//
G[0]=1;
for(int i=1;i<=c0;i++)G[i]=0;
for(int i=1;i<=c;i++)
if(gg[i]>0)
for(int j=c0;j>=gg[i];--j)
Add(G[j],G[j-gg[i]]);
for(int i=1;i<=c0;i++)
Add(G[i],G[i-1]);
//
F[0]=1;
for(int i=1;i<=d0;i++)F[i]=0;
for(int i=1;i<=tp2;i++){
int Num=p2[i].num;
for(int j=d0;j>=Num;--j)
Add(F[j],F[j-Num]);
}
for(int i=1;i<=d0;i++)
Add(F[i],F[i-1]);
//
for(int ty=0;ty<2;ty++)
for(int i=0;i<=c0;i++)
f[0][ty][i][0]=0;
f[0][0][0][0]=1;
int mxx=0;
int now=0,lst=1;
for(int i=1;i<=tp1;i++){
now^=1,lst^=1;
int Num=p1[i].num,oo=p1[i].dis,Dif=rev[i],lv=mxx;
mxx+=Num;
for(int ty=0;ty<2;ty++)
for(int i=0;i<=c0;i++)
for(int j=0;j<=mxx;j++)
f[now][ty][i][j]=0;
for(int ty=0;ty<2;ty++){
int mus=-1;
if(i>1&&p1[i].bel==p1[i-1].bel)mus=ty;
for(int i=c0;i>=0;--i)
for(int j=mxx;j>=0;--j){
if(mus!=1){
if(oo!=0&&i>=Dif&&j>=Num&&j-Num<=lv)Add(f[now][0][i][j],f[lst][ty][i-Dif][j-Num]);
if(oo!=1&&i>=Dif&&j<=lv)Add(f[now][0][i][j],f[lst][ty][i-Dif][j]);
}
if(mus!=0){
if(oo!=2&&j>=Num&&j-Num<=lv)Add(f[now][1][i][j],f[lst][ty][i][j-Num]);
if(oo!=3&&j<=lv)Add(f[now][1][i][j],f[lst][ty][i][j]);
}
}
}
}
int als=0;
for(int ty=0;ty<2;ty++)
for(int i=0;i<=c0;i++)
for(int j=0;j<=mxx;j++){
int val=f[now][ty][i][j];
if(!val)continue;
int cl=max(0,sum-c1-i),cr=c0-i;
int nl=max(0,sum-d1-j),nr=d0-j;
Add(als,mul(val,mul(GG(cl,cr),GF(nl,nr))));
}
printf("%d\n",als);
}
int main()
{
int t;
for(scanf("%d",&t);t;--t)
solve();
return 0;
}