zoukankan      html  css  js  c++  java
  • luogu P5289 [十二省联考2019]皮配 背包

    LINK:皮配

    我承认是一道很难的题目。

    不过对于这道题 部分分的提示显得尤为重要。

    首先是 40分的暴力dp 很容易想 但是不容易写。

    从40分可以发现我们只需要把蓝阵营和鸭派系的人数给存在起来就行了 此时可以获得50分。

    观察题目中存在k==0的情况 可以发现 加入阵营和派系没有什么关系 所以就可以分开的做。

    考虑100分 容易发现有毒的学校就30个 对于这三十个城市单独做暴力dp 剩下的按照上述方法。

    一个难点:可以发现 学校选择的派系是影响城市的 所以感觉这样做不太行。

    把有毒的城放在一起dp 很遗憾这样做复杂度最快也是(nm^2)的 过不了。

    可以发现 对于那些没有毒的学校但有毒的城市来说 没有毒的学校还是可以对于派系随便选的。

    对于有毒的城市选择阵营的时候一起做 选择派系的时候单独做。

    这样还是只做了k个城市。

    设总体的状态为f[i][j]表示前T个城市蓝阵营i人鸭派系j的方案数。

    对于每个城市单独dp的话需要提前处理一个g数组 表示当前城市 前G个学校蓝阵营i人鸭派系j人的方案数。

    进一步的可以发现这个状态可以变成g1[j]表示加入蓝阵营鸭派系j人 g2[j]表示加入红阵营j人的方案数。

    可以发现合并的时候还要枚举一个决策k 所以总复杂度为(km^3)的。

    一个trick 直接让g1和g2代替f数组 然后再进行转移 这样就不需要合并背包了。

    复杂度(km^2). 没开o2速度排在rk8 常数海星. !注意边界问题。

    const int MAXN=1010,maxn=2510;
    int n,T,c,C0,C1,sum,D0,D1,lim1,lim2;
    int sc[MAXN],wc[MAXN],f[maxn][maxn];
    struct wy{int c,s;int op;}t[MAXN];
    int f1[maxn],f2[maxn],g1[maxn][maxn],g2[maxn][maxn];
    inline void add(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
    inline int cmp(wy a,wy b){return a.c<b.c;}
    inline int calc1(int s)
    {
    	int l=max(0,sum-s-C1),r=C0-s;
    	if(l>r)return 0;
    	if(!l)return f1[r];
    	return (f1[r]-f1[l-1]+mod)%mod;
    }
    inline int calc2(int s)
    {
    	int l=max(0,sum-s-D1),r=D0-s;
    	if(l>r)return 0;
    	if(!l)return f2[r];
    	return (f2[r]-f2[l-1]+mod)%mod;
    }
    inline void cle()
    {
    	rep(1,n,i)sc[i]=wc[i]=0;
    	rep(0,max(C0,D0),i)f1[i]=f2[i]=0;
    	rep(0,lim1,i)rep(0,lim2,j)g1[i][j]=g2[i][j]=f[i][j]=0;
    	lim1=lim2=sum=0;
    }
    int main()
    {
    	freopen("1.in","r",stdin);
    	get(T);
    	while(T--)
    	{
    		get(n);get(c);
    		get(C0);get(C1);get(D0);get(D1);
    		rep(1,n,i)
    		{
    			int get(x),get(y);
    			t[i]=(wy){x,y,0};
    			sum+=y;wc[x]+=y;
    		}
    		C0=min(C0,sum);C1=min(C1,sum);
    		D0=min(D0,sum);D1=min(D1,sum);
    		int get(Q);
    		rep(1,Q,i)
    		{
    			int get(x),get(y);
    			op(x)=y+1;sc[c(x)]=1;
    		}
    		f1[0]=f2[0]=1;
    		//f1对城市进行dp f2对学校进行dp.
    		rep(1,c,i)
    		{
    			if(sc[i]||!wc[i])continue;
    			fep(C0,wc[i],j)add(f1[j],f1[j-wc[i]]);
    		}
    		rep(1,n,i)
    		{
    			if(op(i))continue;
    			fep(D0,s(i),j)add(f2[j],f2[j-s(i)]);
    		}
    		//对所有有毒的城市进行dp.
    		//f[i][j]表示蓝阵营i人鸭派系j人的方案数.
    		int flag=1,sum1=0,sum2=0;//分别表示进入阵营和进入派系的人数.
    		sort(t+1,t+1+n,cmp);f[0][0]=1;
    		rep(1,c,G)
    		{
    			int st=flag;
    			while(c(flag)==G&&flag<=n)++flag;
    			int en=flag-1;
    			if(!sc[G])continue;
    			rep(0,lim1,i)rep(0,lim2,j)g2[i][j]=g1[i][j]=f[i][j];
    			rep(st,en,k)//只dp有毒的城市.
    			{
    				if(op(k))
    				{
    					sum2+=s(k);
    					lim2=min(sum2,D0);
    					fep(lim1,0,i)fep(lim2,0,j)
    					{
    						g1[i][j]=((j>=s(k)?g1[i][j-s(k)]*(op(k)!=1):0)+g1[i][j]*(op(k)!=2))%mod;
    						g2[i][j]=((j>=s(k)?g2[i][j-s(k)]*(op(k)!=3):0)+g2[i][j]*(op(k)!=4))%mod;
    					}
    				}
    			}
    			sum1+=wc[G];
    			lim1=min(sum1,C0);
    			fep(lim1,0,i)fep(lim2,0,j)f[i][j]=((i>=wc[G]?g1[i-wc[G]][j]:0)+g2[i][j])%mod;
    		}
    		rep(1,max(C0,D0),i)add(f1[i],f1[i-1]),add(f2[i],f2[i-1]);
    		int ans=0;
    		rep(0,lim1,i)
    		rep(0,lim2,j)
    		add(ans,(ll)f[i][j]*calc1(i)%mod*calc2(j)%mod);
    		put(ans);cle();
    	}
    	return 0;
    }
    
  • 相关阅读:
    POJ 3630 Phone List/POJ 1056 【字典树】
    HDU 1074 Doing Homework【状态压缩DP】
    POJ 1077 Eight【八数码问题】
    状态压缩 POJ 1185 炮兵阵地【状态压缩DP】
    POJ 1806 Manhattan 2025
    POJ 3667 Hotel【经典的线段树】
    状态压缩 POJ 3254 Corn Fields【dp 状态压缩】
    ZOJ 3468 Dice War【PD求概率】
    POJ 2479 Maximum sum【求两个不重叠的连续子串的最大和】
    POJ 3735 Training little cats【矩阵的快速求幂】
  • 原文地址:https://www.cnblogs.com/chdy/p/13127005.html
Copyright © 2011-2022 走看看