• 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;
    }
    
  • 相关阅读:
    费用流
    平面最近点对
    纸牌均分问题
    cdq分治模板
    费解的开关
    斐波那契和排列组合性质
    主席树
    Springboot使用EasyExcel(仅限自己收藏)
    vue项目中h5移动端中通过flex布局实现首尾固定,中间滚动(借鉴)
    vue路由参数的获取、添加和替换
  • 原文地址:https://www.cnblogs.com/chdy/p/13127005.html
Copyright © 2020-2023  润新知