「十二省联考 2019」皮配
传送门
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_{i\in V} 1+y^{s_i} \]对于含有偏好学校的城市,如果它的无偏好学校集合为\(V_1\),它的生成函数是
\[(1\times Prod_0+x^{Sum_V}\times Prod_1)\prod_{i\in 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_{i\in V_1} 1+y^{s_i}\)部分
其中\(x,y\)系数分开来算,这部分的复杂度是\(O(nM)\)
然后我们要求出\((1\times Prod_0+x^{Sum_V}\times Prod_1)\)的乘积,用\(f_{i,j}\)表示\(x^iy^j\)的系数
如果暴力乘的话,每个城市先分成\(Prod_0\)和\(Prod_1\)分别算贡献,再加起来
单个学校合并是\(O(M_1)\)的,因为只有\(30\)个城市,\(s_i\leq 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)<<"\n"
#define dbg3(x) cerr<<#x<<"\n"
#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\n",ans);
}
return 0;
}
Blog来自PaperCloud,未经允许,请勿转载,TKS!