LOJ3051 「十二省联考 2019」皮配

可能是我太菜了,,,感觉这道题除了题面拉了一点之外还是很不错的。

这道题目考察的是独立性

发现如果对于没有任何的限制,两个维度实际上是可以拆开来的分别计算方案数的,最后直接相乘即可,所以我们将没有限制的城市跑关于阵营的 \(\text{dp}\) ,没有限制的学校跑关于派系的 \(\text{dp}\) 。考虑到还存在 \(k\le 30\) 个限制,这些限制我们可以通过暴力的 \(\text{dp}\) 来求解。

关键在于如何复杂度正确的暴力 \(\text{dp}\) 。显然的,我们令 \(f_{i,j}\) 表示蓝阵营选择了 \(i\) 个,鸭派系选择了 \(j\) 个的方案数。此时如果两个维度依旧是一起 \(\text{dp}\) 的,显然会互相影响,我们还需要知道城市的对应阵营,这显然会使得复杂度不正确。

我们考虑阵营是以城市为最小单位进行转移的,派系是以学校为最小单位转移的。我们将存在限制的城市和学校分别提取出来,再将相同城市的有限制学校一起转移,这样我们就可以钦定该城市的阵营,同时暴力转移其中的每一所学校的限制了。

复杂度应该是 \(O(k^2s_iM)\) 的。

#include<bits/stdc++.h>
using namespace std;

const int N=1e3+5,M=2.5e3+5;
const int MOD=998244353;

int ADD(int x,int y){if((x+=y)>=MOD)x-=MOD;return x;}
int SUB(int x,int y){if((x-=y)<0)x+=MOD;return x;}
int TIME(int x,int y){return (int)(1ll*x*y%MOD);}

int n,m,k,tot=0,C[2],D[2];

struct School{int c,f,lim;}a[N];
struct City{int f,lim;vector<School> bag;}b[N];

int f[M],F,g[M],G,h[M][M],H1,H2;
int s[M][M],S1,S2,t[M][M],T1,T2;

int res=0;

int solve(){
	cin>>n>>m>>C[0]>>C[1]>>D[0]>>D[1];
	
	tot=0;
	for(int i=1;i<=n;++i) scanf("%d%d",&a[i].c,&a[i].f),a[i].lim=-1,tot+=a[i].f;

	cin>>k;
	for(int i=1,id,lim;i<=k;++i) scanf("%d%d",&id,&lim),a[id].lim=lim;

	if(tot-C[1]>C[0]||tot-D[1]>D[0]) return printf("0\n"),0;

	for(int i=1;i<=m;++i) b[i].f=0,b[i].lim=-1,b[i].bag.clear();
	for(int i=1;i<=n;++i){
		b[a[i].c].bag.push_back(a[i]);
		b[a[i].c].f+=a[i].f,b[a[i].c].lim=max(b[a[i].c].lim,a[i].lim);
	}

	for(int i=0;i<=F;++i) f[i]=0;
	f[0]=1,F=0;
	for(int i=1;i<=m;++i){
		if(b[i].lim!=-1||!b[i].f) continue;
		F=min(F+b[i].f,C[0]);
		for(int j=F;j>=b[i].f;--j) f[j]=ADD(f[j],f[j-b[i].f]);
	}

	for(int i=0;i<=G;++i) g[i]=0;
	g[0]=1,G=0;
	for(int i=1;i<=n;++i){
		if(a[i].lim!=-1) continue;
		G=min(G+a[i].f,D[0]);
		for(int j=G;j>=a[i].f;--j) g[j]=ADD(g[j],g[j-a[i].f]);
	}

	for(int i=0;i<=H1;++i) for(int j=0;j<=H2;++j) h[i][j]=0;
	h[0][0]=1,H1=H2=0;
	for(int i=1;i<=m;++i){
		if(b[i].lim==-1||!b[i].f) continue;
		
		for(int c=0;c<=S1;++c) for(int d=0;d<=S2;++d) s[c][d]=0;
		S1=min(H1+b[i].f,C[0]),S2=H2;

		for(int c=b[i].f;c<=S1;++c)	for(int d=0;d<=S2;++d) s[c][d]=h[c-b[i].f][d];

		for(int j=0;j<(int)b[i].bag.size();++j){
			if(b[i].bag[j].lim==-1) continue;
			if(b[i].bag[j].lim==0);
			else if(b[i].bag[j].lim==1){
				S2=min(S2+b[i].bag[j].f,D[0]);
				for(int c=0;c<=S1;++c){
					for(int d=S2;d>=b[i].bag[j].f;--d)
						s[c][d]=s[c][d-b[i].bag[j].f];
					for(int d=b[i].bag[j].f-1;d>=0;--d) s[c][d]=0;
				}
			}
			else{
				S2=min(S2+b[i].bag[j].f,D[0]);
				for(int c=0;c<=S1;++c)
					for(int d=S2;d>=b[i].bag[j].f;--d)
						s[c][d]=ADD(s[c][d],s[c][d-b[i].bag[j].f]);
			}
		}

		for(int c=0;c<=T1;++c) for(int d=0;d<=T2;++d) t[c][d]=0;
		T1=H1,T2=H2;

		for(int c=0;c<=T1;++c) for(int d=0;d<=T2;++d) t[c][d]=h[c][d];

		for(int j=0;j<(int)b[i].bag.size();++j){
			if(b[i].bag[j].lim==-1) continue;
			if(b[i].bag[j].lim==2);
			else if(b[i].bag[j].lim==3){
				T2=min(T2+b[i].bag[j].f,D[0]);
				for(int c=0;c<=T1;++c){
					for(int d=T2;d>=b[i].bag[j].f;--d)
						t[c][d]=t[c][d-b[i].bag[j].f];
					for(int d=b[i].bag[j].f-1;d>=0;--d) t[c][d]=0;
				}
			}
			else{
				T2=min(T2+b[i].bag[j].f,D[0]);
				for(int c=0;c<=T1;++c)
					for(int d=T2;d>=b[i].bag[j].f;--d)
						t[c][d]=ADD(t[c][d],t[c][d-b[i].bag[j].f]);
			}
		}

		H1=max(S1,T1),H2=max(S2,T2);
		for(int c=0;c<=H1;++c) for(int d=0;d<=H2;++d) h[c][d]=ADD(s[c][d],t[c][d]);
	}

	for(int i=1;i<=F;++i) f[i]=ADD(f[i],f[i-1]);
	for(int i=1;i<=G;++i) g[i]=ADD(g[i],g[i-1]);

	res=0;

	for(int i=0;i<=H1;++i){
		for(int j=0;j<=H2;++j){
			int l=max(0,tot-C[1]-i),r=min(F,C[0]-i),tmp;
			int L=max(0,tot-D[1]-j),R=min(G,D[0]-j),TMP;
			if(l==0) tmp=f[r];else tmp=SUB(f[r],f[l-1]);
			if(L==0) TMP=g[R];else TMP=SUB(g[R],g[L-1]);
			if(l<=r&&L<=R) res=ADD(res,TIME(h[i][j],TIME(tmp,TMP)));
		}
	}

	return printf("%d\n",res),0;
}
/*
玖億玖仟捌佰贰拾肆萬肆仟叁佰伍拾叁
*/

int main(){
	// freopen("1.in","r",stdin);

	int T;cin>>T;while(T--) solve();
	return 0;
}

/*
有四个篮子和若干个带权带颜色的物品,相同颜色的物品需要放到同一组篮子中,存在一些物品有限制,不会放入一个篮子中。
同时四个篮子内部也存在一个四元环的限制,表示边上两点的和不超过一个定值。
-------------------------------------------------------------------------------------------------------
焯,考虑独立性,如果没有其余限制的话,找到合法的阵营划分方案数,合法的派系划分方案数,直接相乘就是答案了。
我们必然存在暴力的做法,统计其中一个阵营和派系的人数,具体的,我们可以尝试将学校按照城市排序,然后每次记录上一个学校的阵营选择,然后统计即可。
然后正解就是把上面两个方法拼起来。。。

感觉合并的时候细节有点多,因为要考虑前面的同城市的限制。

双线程背包,大体想法就是先枚举城市,将城市放入之后再枚举这个城市的学校,放入即可。
*/
posted @ 2022-03-11 07:21  Point_King  阅读(27)  评论(0编辑  收藏  举报