传送门
Solution
这是一道背包计数问题,所以可以从生成函数的角度来理解。
每个选手可选择的是阵营(0/1),以及派系(0/1)
如果一个城市选择了第(i)阵营第(j)派系,那么相当于分别让阵营(i)和派系(j)的代价(+s_i)
设一个没有任何限制的学校的生成函数为(1+x^{s_i}+y^{s_i}+x^{s_i}y^{s_i}),其中(x)表示阵营(1),(y)表示派系(1)
对于一个城市的学校集合为(V),假设它没有偏好学校,它的生成函数就是
[(1+x^{Sum_{V}})prod_{iin V} 1+y^{s_i} ]对于含有偏好学校的城市,如果它的无偏好学校集合为(V_1),它的生成函数是
[(1 imes Prod_0+x^{Sum_V} imes Prod_1)prod_{iin V_1} 1+y^{s_i} ](Prod_1)和(Prod_0)是偏好城市的生成函数的积
假如这个城市不加入阵营(1)派系(0),那么它在(Prod_0)的贡献是((1+y^{s_i})),对(Prod_1)的贡献是(y^{s_i})
答案是所有城市的生成函数的积中满足(x)的次和(y)的次数在范围([Sum-C_0,C_1]),和([Sum-D_0,D_1])的项的系数
对于没有偏好的城市分别计算前部分((1+x^{Sum_V}))和后部分(prod 1+y^{s_i})的系数
乘上有偏好学校城市的(prod_{iin V_1} 1+y^{s_i})部分
其中(x,y)系数分开来算,这部分的复杂度是(O(nM))
然后我们要求出((1 imes Prod_0+x^{Sum_V} imes Prod_1))的乘积,用(f_{i,j})表示(x^iy^j)的系数
如果暴力乘的话,每个城市先分成(Prod_0)和(Prod_1)分别算贡献,再加起来
单个学校合并是(O(M_1))的,因为只有(30)个城市,(s_ileq 10),所以(M_1=300)。
城市和城市合并是(O(nM_1))的,所以总复杂度是(O(knM_1))
最后枚举(f_{i,j}),求出(x,y)的次数区间,前缀和求区间和,相乘计入答案即可
ps: 算的时候要把没有学校的城市去掉,
不然20分欢迎你
Code
#include<bits/stdc++.h>
#define ll long long
#define dbg1(x) cerr<<#x<<"="<<(x)<<" "
#define dbg2(x) cerr<<#x<<"="<<(x)<<"
"
#define dbg3(x) cerr<<#x<<"
"
#define ME(x) memset(x,0,sizeof x)
using namespace std;
#define reg register
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
const int MM=2505,MN=1005,MS=11,MK=31,P=998244353;
int Mul(int x,int y){return (1ll*x*y)%P;}
int Add(int x,int y){return (x+y)%P;}
bool a[MN];
int n,c,C0,C1,D0,D1,b[MN],s[MN],k,p[MN],x[MM],y[MM],f[MM][MK*MS],f1[MM][MK*MS],sum[MN];
void pro(int *r,int ed,int ty,int i){for(;~i;--i)r[i]=Add(r[i]*ty,i>=ed?r[i-ed]:0);}
int main()
{
int Case=read();
while(Case--)
{
n=read(),c=read();C0=read(),C1=read(),D0=read(),D1=read();
int i,j,M=max(max(C0,C1),max(D0,D1)),Sx=0,Sy=0,ans=0,S=0;
ME(x);ME(y);ME(f);ME(f1);ME(sum);ME(a);
for(i=1;i<=n;++i) b[i]=read(),s[i]=read(),p[i]=-1,sum[b[i]]+=s[i],S+=s[i];
for(k=read(),i=1;i<=k;++i) j=read(),p[j]=read(),a[b[j]]=1;
for(x[0]=i=1,j=0;i<=c;++i)if(!a[i]&&sum[i])j=min(M,j+sum[i]),pro(x,sum[i],1,j);
for(i=1;i<=M;++i)x[i]=Add(x[i],x[i-1]);
for(y[0]=i=1,j=0;i<=n;++i)if(!~p[i])j=min(M,j+s[i]),pro(y,s[i],1,j);
for(i=1;i<=M;++i)y[i]=Add(y[i],y[i-1]);
for(f[0][0]=i=1;i<=c;++i)if(a[i])
{
reg int u,v;
for(u=0;u<=Sx;++u)for(v=0;v<=Sy;++v)f1[u][v]=f[u][v];
for(j=1;j<=n;++j)if(b[j]==i&&~p[j])
{
Sy=min(Sy+s[j],M);
if(p[j]<2){for(u=Sx;~u;--u)pro(f1[u],s[j],1,Sy),!p[j]?(pro(f[u],s[j],0,Sy),1):1;}
else{for(u=Sx;~u;--u)pro(f[u],s[j],1,Sy),(p[j]<3)?(pro(f1[u],s[j],0,Sy),1):1;}
}
Sx=min(Sx+sum[i],M);
for(u=Sx;~u;--u)for(v=Sy;~v;--v)f[u][v]=Add(u>=sum[i]?f1[u-sum[i]][v]:0,f[u][v]);
}
for(i=Sx;~i;--i)for(j=Sy;~j;--j)if(f[i][j])
{
int lx=max(S-C0-i,0),rx=C1-i,ly=max(S-D0-j,0),ry=D1-j;if(lx>rx||ly>ry)continue;
ans=Add(ans,Mul(f[i][j],Mul(Add(x[rx],P-(lx?x[lx-1]:0)),Add(y[ry],P-(ly?y[ly-1]:0)))));
}
printf("%d
",ans);
}
return 0;
}
Blog来自PaperCloud,未经允许,请勿转载,TKS!