「十二省联考 2019」皮配

「十二省联考 2019」皮配

传送门

Loj

题解

这个题目其实很妙,真的.

首先考虑一个最简单的\(dp\),设\(f_{i,j,k}\)表示前\(i\)个学校,有\(j\)个人选择了蓝阵营,\(k\)个人选择了鸭派系.那么很显然可以背包转移.

如果没有限制,那么可以直接把城市的决策和学校的决策卷积.

这个时候有了限制,还是考虑分开做.你发现这个\(k\)贼小,而且\(s_i\)也很小.

没有限制的学校和城市,可以直接背包出来.

如果有了限制,考虑用最简单的\(dp\)背包,这样子的复杂度就是\(ks\)的一个背包,所以可以过.

代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define ll long long
#define REP(a,b,c) for(int a=b;a<=c;a++)
#define re register
#define file(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
typedef pair<int,int> pii;
#define mp make_pair
inline int gi()
{
	int f=1,sum=0;char ch=getchar();
	while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
	return f*sum;
}
const int N=1010,M=3010,Mod=998244353;
int n,c,C0,C1,D0,D1,s[N],b[N],S[N],lim[N],LIM[N],g1[M],g2[M],sum,f[M][M],g[M][M],k;
vector<int>G[N];
int calc(int x,int y)
{
	int l1=max(0,sum-x-C1),r1=C0-x;
	int l2=max(0,sum-y-D1),r2=D0-y;
	if(l2>r2||l1>r1)return 0;
	return 1ll*(g2[r1]-(l1?g2[l1-1]:0)+Mod)*(g1[r2]-(l2?g1[l2-1]:0)+Mod)%Mod;
}
void solve()
{
	n=gi();c=gi();C0=gi();C1=gi();D0=gi();D1=gi();sum=0;
	for(int i=1;i<=n;i++)
	{
		b[i]=gi();s[i]=gi();
		sum+=s[i],S[b[i]]+=s[i];
	}
	k=gi();
	for(int i=1;i<=n;i++)lim[i]=-1;
	for(int i=1;i<=c;i++)LIM[i]=-1;
	for(int i=1;i<=k;i++)
	{
		int id=gi(),p=gi();lim[id]=LIM[b[id]]=p;
		G[b[id]].push_back(id);
	}
	int s1=0,s2=0;g1[0]=g2[0]=1;
	for(int i=1;i<=n;i++)
	{
		s1+=s[i];if(~lim[i])continue;
		for(int j=min(D0,s1);j>=s[i];j--)g1[j]=(g1[j]+g1[j-s[i]])%Mod;
	}
	for(int i=1;i<=c;i++)
	{
		s2+=S[i];if(~LIM[i]||!S[i])continue;
		for(int j=min(C0,s2);j>=S[i];j--)g2[j]=(g2[j]+g2[j-S[i]])%Mod;
	}
	for(int i=1;i<=D0;i++)g1[i]=(g1[i]+g1[i-1])%Mod;
	for(int i=1;i<=C0;i++)g2[i]=(g2[i]+g2[i-1])%Mod;
	s1=s2=0;f[0][0]=1;
	for(int u=1;u<=c;u++)
		if(~LIM[u]&&S[u])
		{
			for(int j=0;j<=min(s1,C0);j++)
				for(int k=0;k<=min(D0,s2);k++)g[j][k]=f[j][k];
			for(int i:G[u])
			{
				s2+=s[i];
				int t0=(lim[i]!=0),t1=(lim[i]!=1),t2=(lim[i]!=2),t3=(lim[i]!=3);
				for(int j=min(s1,C0);~j;j--)
					for(int k=min(s2,D0);~k;k--)
						if(k>=s[i])
						{
							f[j][k]=(f[j][k]*t1+f[j][k-s[i]]*t0)%Mod;
							g[j][k]=(g[j][k]*t3+g[j][k-s[i]]*t2)%Mod;
						}
						else f[j][k]=f[j][k]*t1,g[j][k]=g[j][k]*t3;
			}
			s1+=S[u];
			for(int j=min(C0,s1);~j;j--)
				for(int k=min(D0,s2);~k;k--)
					if(j>=S[u])f[j][k]=(f[j-S[u]][k]+g[j][k])%Mod;
					else f[j][k]=g[j][k];
		}
	int ans=0;
	for(int j=0;j<=C0;j++)
		for(int k=0;k<=D0;k++)
			if(f[j][k])ans=(ans+1ll*f[j][k]*calc(j,k)%Mod)%Mod;
	printf("%d\n",ans);
	for(int j=0;j<=C0;j++)for(int k=0;k<=D0;k++)f[j][k]=g[j][k]=0;
	for(int i=0;i<=D0;i++)g1[i]=0;
	for(int i=0;i<=C0;i++)g2[i]=0;
	for(int i=1;i<=n;i++)s[i]=0;
	for(int i=1;i<=c;i++)S[i]=0,G[i].clear();
}
int main()
{
	int T=gi();
	while(T--)solve();
	return 0;
}
posted @ 2020-05-07 21:11  fexuile  阅读(183)  评论(0编辑  收藏  举报