「十二省联考 2019」皮配

题目大意

avatar

题解

首先,可以想到一个\(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;
}
posted @ 2019-04-08 17:56  蒟蒻小果冻  阅读(791)  评论(0编辑  收藏  举报