bzoj 5498: [2019省队联测]皮配【dp】

是个神仙dp……
参考:https://www.luogu.org/blog/xzz-233/solution-p5289
设f[i][j][k]是前i个有限制的城市,所有学校中选蓝色阵营有j人,有限制的学校中鸭派系有k人的方案数;g[i][j]是前i个没有限制的城市,蓝色阵营有j人的方案数;h[i][j]是前i个没有限制的学校,鸭派系有j人的方案数
f的转移枚举当前阵营
全dp出来之后,把gh前缀和处理,然后一段一段的和f合并加到ans上

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=2505,mod=998244353;
int T,n,c,c0,c1,d0,d1,k,m,ss,ans,b[N],s[N],f[N][N],f1[N][N],f2[N][N],g[N],h[N],ki[N],kp[N],v[N],ct[N],sum[N],sm[N];
vector<int>sc[N];
void jia(int &x,int y)
{
	if((x+=y)>=mod)
		x-=mod;
}
int read()
{
	int r=0,f=1;
	char p=getchar();
	while(p>'9'||p<'0')
	{
		if(p=='-')
			f=-1;
		p=getchar();
	}
	while(p>='0'&&p<='9')
	{
		r=r*10+p-48;
		p=getchar();
	}
	return r*f;
}
int main()
{
	T=read();
	while(T--)
	{
		memset(f,0,sizeof(f));
		memset(f1,0,sizeof(f1));
		memset(f2,0,sizeof(f2));
		memset(g,0,sizeof(g));
		memset(h,0,sizeof(h));
		memset(sm,0,sizeof(sm));
		memset(sum,0,sizeof(sum));
		memset(ct,0,sizeof(ct));
		for(int i=0;i<N;i++)
			sc[i].clear();
		ss=0,ans=0;
		n=read(),c=read(),c0=read(),c1=read(),d0=read(),d1=read();
		for(int i=1;i<=n;i++)
			b[i]=read(),s[i]=read(),sum[b[i]]+=s[i],ss+=s[i];
		m=k=read();
		for(int i=1;i<=n;i++)
			v[i]=-1;
		for(int i=1;i<=k;i++)
		{
			ki[i]=read(),kp[i]=read();
			ct[i]=b[ki[i]];
			sc[b[ki[i]]].push_back(ki[i]);
			v[ki[i]]=kp[i];
		}
		sort(ct+1,ct+1+m);
		m=unique(ct+1,ct+1+m)-ct-1;
		for(int i=1;i<=n;i++)
			if(v[i]!=-1)
				sm[b[i]]+=s[i];
		f[0][0]=1;
		for(int i=1,s1=0,s2=0;i<=m;i++)
		{
			s1=min(c0,s1+sum[ct[i]]),s2=min(d0,s2+sm[ct[i]]);
			for(int o=0;o<=1;o++)
			{
				for(int j=0;j<=s1;j++)
					for(int k=0;k<=s2;k++)
						f1[j][k]=f[j][k];
				for(int l=0,len=sc[ct[i]].size();l<len;l++)
				{
					int x=sc[ct[i]][l];
					for(int j=s1;j>=0;j--)
						for(int k=s2;k>=0;k--)
						{
							if(o==0)
							{
								f1[j][k]=0;
								if(v[x]!=1&&j>=s[x])
									jia(f1[j][k],f1[j-s[x]][k]);
								if(v[x]!=0&&j>=s[x]&&k>=s[x])
									jia(f1[j][k],f1[j-s[x]][k-s[x]]);
							}
							else
							{
								if(v[x]==3)
									f1[j][k]=0;
								if(v[x]!=2&&k>=s[x])
									jia(f1[j][k],f1[j][k-s[x]]);
							}
						}
				}
				if(o==0)
				{
					int nw=sum[ct[i]]-sm[ct[i]];
					for(int j=s1;j>=0;j--)
						for(int k=s2;k>=0;k--)
						{
							if(j>=nw)
								f1[j][k]=f1[j-nw][k];
							else
								f1[j][k]=0;
						}
					for(int j=0;j<=s1;j++)
						for(int k=0;k<=s2;k++)
							f2[j][k]=f1[j][k];
				}
				else
				{
					for(int j=0;j<=s1;j++)
						for(int k=0;k<=s2;k++)
							f[j][k]=(f1[j][k]+f2[j][k])%mod;
				}
			}
		}
		g[0]=1;
		for(int i=1,smm=0;i<=c;i++)
			if(sc[i].empty()&&sum[i])
			{
				smm=min(c0,smm+sum[i]);
				for(int j=smm;j>=0;j--)
					if(j>=sum[i])
						jia(g[j],g[j-sum[i]]);
			}
		h[0]=1;
		for(int i=1,smm=0;i<=n;i++)
			if(v[i]==-1)
			{
				smm=min(d0,smm+s[i]);
				for(int j=smm;j>=0;j--)
					if(j>=s[i])
						jia(h[j],h[j-s[i]]);
			}
		for(int i=1;i<=c0;i++)
			jia(g[i],g[i-1]);
		for(int i=1;i<=d0;i++)
			jia(h[i],h[i-1]);
		for(int i=0;i<=c0;i++)
			if(ss-i-c1-1<c0-i)
				for(int j=0;j<=d0;j++)
					if(ss-j-d1-1<d0-j)
						jia(ans,1ll*f[i][j]*(g[c0-i]-(ss-i-c1-1>=0?g[ss-i-c1-1]:0)+mod)%mod*(h[d0-j]-(ss-j-d1-1>=0?h[ss-j-d1-1]:0)+mod)%mod);
		printf("%d
",(ans+mod)%mod);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/lokiii/p/10712364.html