zoukankan      html  css  js  c++  java
  • [提高组集训2021] 妹妹卡组

    一、题目

    妹妹 ( t Oneindark) 给了你 (n) 个卡组,对于每个卡组有 (k_i) 个卡牌,其中第 (j) 个卡牌的大小是 (j),价值是 (a_{i,j}),每个卡组只能选取一张卡牌。

    如果卡牌栏的大小为 (t),那么能获得的最大价值是多少,你需要对 (tin[n,sum k_i]) 都分别求一次,因为 ( t Oneindark) 会向你撒娇,所以你需要快速把它们都求出来~~

    (nleq 10^5,kleq 5)

    二、解法

    (f(i,j)) 为考虑前 (i) 个卡组大小为 (j) 的最大价值,首先有一个根本想不到的结论:把 (j) 这一维模 (12) 意义下分组后,每一组的 (dp) 值关于 (j) 有凸性,设 (D=12),证明:

    首先给出引理:对于若干个和为 (24) 且在 (in{0,1,2,3,4}) 中的元素,可以划分成和为 (12) 的两组。

    我们首先把卡牌的大小都减去 (1)(方便套用引理),然后考虑 (f(x-D)) 调整到 (f(x+D)) 的过程,设第 (i) 组卡牌的变化量是 (d_i),有 (sum d_i=24),且 (d_iin[-4,4])

    那么我们可以把若干个 (d_i) 组合起来,使得和在 (in[0,4]) 中,然后我们把 (d_i) 划分成两组,代价变化多的一组就应用到 (f(x-D))(f(x)) 这个过程上,所以有:

    [f(x)-f(x-D)geq f(x+D)-f(x) ]

    知道此结论我们把第一维分治,问题变成了合并两个 (dp) 数组,那么我们花费 (O(D^2)) 枚举两个组,因为每组内部有相同的凸性,所以可以双指针合并(选增量大的那个),时间复杂度 (O(frac{len}{D})),所以总时间复杂度 (O(Dcdot (nk)log nk))

    三、总结

    对于可以快速合并的背包,可以用分治的方法优化。

    但是这个凸性真的不太明白啊

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    #define int long long
    const int M = 200005;
    const int D = 12;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,k[M],sum,dp[5*M],a[M][5];
    void div(int l,int r,int *dp)
    {
    	if(l==r)
    	{
    		for(int i=0;i<k[l];i++)
    			dp[i]=a[l][i];
    		return ;
    	}
    	int mid=(l+r)>>1,ll=(mid-l+1)*5,lr=(r-mid)*5;
    	int f[ll+5],g[lr+5];
    	memset(f,-0x3f,sizeof f);
    	memset(g,-0x3f,sizeof g);
    	div(l,mid,f);div(mid+1,r,g);
    	for(int x=0;x<D;x++) for(int y=0;y<D;y++)
    	{
    		if(x>=ll || y>=lr) continue;
    		dp[x+y]=max(dp[x+y],f[x]+g[y]);
    		int i=x,j=y;
    		while(i+D<ll || j+D<lr)
    		{
    			int f1=i+D<ll,f2=j+D<lr;
    			if(!f1 || (f2 && g[j+D]-g[j]>f[i+D]-f[i]))
    			{
    				dp[i+j+D]=max(dp[i+j+D],f[i]+g[j+D]);
    				j+=D;
    			}
    			else
    			{
    				dp[i+j+D]=max(dp[i+j+D],f[i+D]+g[j]);
    				i+=D;
    			}
    		}
    	}
    }
    signed main()
    {
    	freopen("fake.in","r",stdin);
    	freopen("fake.out","w",stdout);
    	n=read();
    	for(int i=1;i<=n;i++)
    	{
    		sum+=k[i]=read();
    		for(int j=0;j<k[i];j++)
    			a[i][j]=read();
    	}
    	memset(dp,-0x3f,sizeof dp);
    	div(1,n,dp);
    	for(int i=0;i<=sum-n;i++)
    		printf("%lld ",dp[i]);
    	puts("");
    }
    
  • 相关阅读:
    第10组 Beta冲刺 (3/5)
    第10组 Beta冲刺 (2/5)
    第10组 Beta冲刺 (1/5)
    软工实践个人总结
    第03组 每周小结(3/3)
    第03组 每周小结(2/3)
    第03组 每周小结(1/3)
    第03组 Beta冲刺 总结
    第03组 Beta冲刺 (5/5)
    第03组 Beta冲刺 (4/5)
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15367355.html
Copyright © 2011-2022 走看看