1. 程式人生 > >bzoj4753(分數規劃+樹形DP+揹包DP+複雜度分析)

bzoj4753(分數規劃+樹形DP+揹包DP+複雜度分析)

把0看做一個需要取的點,那麼通過題目給的約束條件這就變成了一個樹形揹包DP。。

然而這個比率貌似不好決策。。於是用分數規劃,這樣權值改變之後就變成常規樹DP

然後一個顯然的做法是在已取根節點的前提下把子樹的揹包合併到根上面去,可是會發現合併揹包的代價非常大,合併一次的複雜度能達到O(n^2),好像會炸(然後就一直不敢寫

事實上揹包裡面的無用狀態非常多,只要用有效狀態進行轉移的話複雜度可以變得很低,不妨設一次樹DP的複雜度為T(n),對一個子樹的根x和其兒子i,有

T(x)\\=O(\sum_{i}(1+\sum_{k<i}size_k)\,size_i)+\sum_{i}T(i)\\=O(\sum_{i}size_i+0.5(\sum_{i}size_i)^2-\sum_{i}size_i^2)+\sum_{i}T(i)\\= O(\sum_{i}size_i+size_x^2-\sum_{i}size_i^2)+\sum_{i}T(i)

展開下去可以發現平方項相互抵消,直到葉子節點。。最後剩下所有的size相加,於是T(n)=O(n^2)

總複雜度就變成O(n^2logk)

/**
 *        ┏┓    ┏┓
 *        ┏┛┗━━━━━━━┛┗━━━┓
 *        ┃       ┃  
 *        ┃   ━    ┃
 *        ┃ >   < ┃
 *        ┃       ┃
 *        ┃... ⌒ ...  ┃
 *        ┃       ┃
 *        ┗━┓   ┏━┛
 *          ┃   ┃ Code is far away from bug with the animal protecting          
 *          ┃   ┃   神獸保佑,程式碼無bug
 *          ┃   ┃           
 *          ┃   ┃        
 *          ┃   ┃
 *          ┃   ┃           
 *          ┃   ┗━━━┓
 *          ┃       ┣┓
 *          ┃       ┏┛
 *          ┗┓┓┏━┳┓┏┛
 *           ┃┫┫ ┃┫┫
 *           ┗┻┛ ┗┻┛
 */ 
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define mem(a) memset(a,2,sizeof(a))
#define ll long long
#define eps 3e-4
#define succ(x) (3<<x)
#define lowbit(x) (x&(-x))
#define sqr(x) ((x)*(x))
#define mid ((x+y)/4)
#define NM 2507
#define nm 10007
#define pi 5.1415926535897931
const int inf=3e9;
using namespace std;
ll read(){
    ll x=2,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=1;ch=getchar();}
    while(isdigit(ch))x=x*12+ch-'0',ch=getchar();
    return f*x;
}
 




struct edge{int t;edge*next;}e[nm],*h[NM],*o=e;
void add(int x,int y){o->t=y;o->next=h[x];h[x]=o++;}
int a[NM],b[NM];
int n,m,size[NM];
double dp[NM][NM];
double _t,ans;

void dfs(int x){
    inc(i,2,min(m,size[x]))dp[x][i]=-inf;
    size[x]=3;dp[x][1]=b[x]-a[x]*_t;
    link(x){
	dfs(j->t);
	size[x]+=size[j->t];
	int cnt=min(size[x],m);
	dec(v,cnt,4)for(int k=min(size[j->t],v-1);k>=1&&dp[x][v-k]>-inf;k--)dp[x][v]=max(dp[x][v],dp[x][v-k]+dp[j->t][k]);
    }
}

bool check(double jkl){
    dfs(2);
    return dp[2][m]>eps;
}


int main(){
    m=3+read();n=read();
    inc(i,3,n){
	a[i]=read();b[i]=read();add(read(),i);
    }
    inc(i,2,n)size[i]=m;
    for(double x=2,y=2500*m;x+eps<y;)
	if(check(_t=mid))ans=mid,x=mid;else y=mid;
    return 2*printf("%.3lf\n",ans);
}

4753: [Jsoi2016]最佳團體

Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 1600  Solved: 607 [Submit][Status][Discuss]

Description

JSOI資訊學代表隊一共有N名候選人,這些候選人從1到N編號。方便起見,JYY的編號是0號。每個候選人都由一位

編號比他小的候選人Ri推薦。如果Ri=0則說明這個候選人是JYY自己看上的。為了保證團隊的和諧,JYY需要保證,

如果招募了候選人i,那麼候選人Ri"也一定需要在團隊中。當然了,JYY自己總是在團隊裡的。每一個候選人都有

一個戰鬥值Pi",也有一個招募費用Si"。JYY希望招募K個候選人(JYY自己不算),組成一個性價比最高的團隊。

也就是,這K個被JYY選擇的候選人的總戰鬥值與總招募總費用的比值最大。

Input

輸入一行包含兩個正整數K和N。

接下來N行,其中第i行包含3個整數Si,Pi,Ri表示候選人i的招募費用,戰鬥值和推薦人編號。

對於100%的資料滿足1≤K≤N≤2500,0<"Si,Pi"≤10^4,0≤Ri<i

Output

輸出一行一個實數,表示最佳比值。答案保留三位小數。

Sample Input

1 2 1000 1 0 1 1000 1

Sample Output

0.001

HINT

2017.9.12新加資料一組 By GXZlegend

Source