• arc096_f Sweet Alchemy


    arc096_f Sweet Alchemy

    https://atcoder.jp/contests/arc096/tasks/arc096_d

    UwTa8I.png

    Tutorial

    这是一个树的结构,可以差分一下,令(c'_i=c_i-c_{p_i}),特别的(c'_1=c_1),那么限制变为了(forall i in [2,N],c_i le D),而(c_i)的意义为将子树中的所有甜甜圈制作(c_i)次.

    那么现在问题可以描述为有(N)个物品,每个物品有(D_i)个,价值为(Y_i),体积为(X_i),问大小为(X)的背包的最大价值,不必装满.其中除了(N,Y_i)之外都很大.

    首先,可以贪心的考虑,将物品按单位价值(dfrac {Y_i}{X_i})排序,但是显然,背包问题贪心是不优的.

    但是发现,如果对于排序后的两个物品(p<q),若(p)有至少(N)个没选,(q)选择了至少(N)个.那么从背包中拿掉(Y_p)(q)物品,加入(Y_q)(p)物品,此时价值不变,体积不会更劣.

    所以可以从每种物品中拿出(min(D_i,N))个物品,对这些物品做一次以价值为基准的多重背包,然后枚举价值,对于剩下的物品贪心求解即可.

    第一个背包中的价值最大是(N^3),所以总复杂度(O(N^4 log N))

    Code

    #include <algorithm>
    #include <cstdio>
    #include <iostream>
    #define debug(...) fprintf(stderr,__VA_ARGS__)
    using namespace std;
    inline char gc() {
    	return getchar();
    	static char buf[100000],*l=buf,*r=buf;
    	return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
    }
    template<class T> void rd(T &x) {
    	x=0; int f=1,ch=gc();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    	while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=gc();}
    	x*=f;
    }
    template<class T> inline bool Cmax(T &x,T y) {return x<y?x=y,1:0;}
    template<class T> inline bool Cmin(T &x,T y) {return x>y?x=y,1:0;}
    typedef long long ll;
    const int inf=1e9+1;
    const int maxN=50+5;
    const int maxn=maxN*maxN*maxN;
    int N,X,D,p[maxN];
    int num0[maxN],num1[maxN];
    int dp[maxn];
    struct node {
    	ll x; int y,id;
    	inline bool operator <(const node &other) const {
    		return y*other.x>other.y*x;
    	}
    } a[maxN];
    int sol(int X) {
    	int an=0;
    	for(int i=1;i<=N;++i) {
    		int d=min(num0[i],int(X/a[i].x));
    		an+=d*a[i].y,X-=d*a[i].x;
    	}
    	return an;
    }
    int main() {
    	rd(N),rd(X),rd(D);
    	rd(a[1].x),a[1].id=1;
    	for(int i=2;i<=N;++i) rd(a[i].x),rd(p[i]),a[i].id=i;
    	for(int i=N;i>=1;--i) {
    		++a[i].y;
    		a[p[i]].x+=a[i].x,a[p[i]].y+=a[i].y;
    	}
    	sort(a+1,a+N+1);
    	for(int i=1;i<=N;++i) {
    		num0[i]=a[i].id==1?inf:D;
    		num1[i]=min(num0[i],N),num0[i]-=num1[i];
    	}
    	int n=N*N*N;
    	for(int i=1;i<=n;++i) dp[i]=inf;
    	for(int i=1;i<=N;++i) {
    		for(int j=1,t=num1[i];t;t-=j,j=min(j<<1,t)) {
    			int v=a[i].y*j; ll w=a[i].x*j;
    			for(int k=n;k>=v;--k) dp[k]=min((ll)dp[k],dp[k-v]+w);
    		}
    	}
    	int an=0;
    	for(int i=0;i<=n;++i) if(dp[i]<=X) {
    //		debug("%d %d
    ",i,dp[i]);
    		Cmax(an,i+sol(X-dp[i]));
    	}
    	printf("%d
    ",an);
    	return 0;
    } 
    
  • 相关阅读:
    如何在同一窗口打开多个终端并实现快捷键切换
    Django基础八之cookie和session
    jQuery操作cookie
    Django基础九之中间件
    Django基础七之Ajax
    Mac下打开/usr/local目录
    Django基础二之URL路由系统
    json path espressions的语法学习
    如何查看bug属于前端还是后端
    python random生成随机手机号
  • 原文地址:https://www.cnblogs.com/ljzalc1022/p/13307238.html
Copyright © 2020-2023  润新知