「十二省联考 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!

posted @ 2019-08-03 22:27  PaperCloud  阅读(357)  评论(0编辑  收藏  举报