有n堆宝藏,每一堆宝藏有一个挖掘所需要的时间ti,有一个价值qi。
现在是做一个宝藏图。这个宝藏图是这样的,宝藏图的形状是一棵二叉树,二叉树刚好有k个叶子结点,从n堆宝藏中选k堆放到二叉树的叶子结点上,每个叶子结点只能放一堆宝藏,每堆宝藏只能放到一个叶子结点上。然后派k个人去找宝藏,每个人会分配到一个宝藏供他查找,在这k个宝藏中,每一个宝藏都会被分配给某一个人,k个人从根结点出发,每个人从当前结点往相邻结点走的时候要花费一个单位的时间,到达他指定的宝藏的时候,他要花费那个宝藏所需要的挖掘时间去挖掘那个宝藏,然后他获得那个宝藏的时间就是他行走的时间+挖掘时间。但是大家必须在T的时间内得到宝藏,所以现在要求在大家能在T时间内得到宝藏的情况下,使得宝藏的总价值最大。
注意:可以不经过任何中间结点直接到达宝藏进行挖掘(样例二)。
Input
单组测试数据。
第一行包含两个整数n 和T (1 ≤ n,T ≤ 100000),表示宝藏的数目和时间限制。
接下来n行,每行有两个整数ti, qi (1 ≤ ti ≤ T, 1 ≤ qi ≤ 1000),第i个宝藏的挖掘时间和它的价值。
Output
输出最大的宝藏总价值。
连暴力都不会写系列。
cf官网给出了O(n^2T)的做法。。。从底往上一层一层算,f[h][w]表示第h层,这层已确定要有w个节点。
每次枚举这一层有x个叶子节点,就能从f[h][w]转移到f[h-1][(w+x+1)/2]...
之后看了cf上别人的AC代码。。在同一层里直接贪心把相邻最大的连向同个父亲就好了.....
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 #include<cmath> 7 #include<cstdlib> 8 #define ll long long 9 #define ull unsigned long long 10 #define ui unsigned int 11 #define d double 12 #define ld long double 13 using namespace std; 14 const int maxn=100233,modd=1000000007; 15 struct zs{int t,v;}a[maxn]; 16 int mp[2][maxn],num[2]; 17 int i,j,k,n,m; 18 19 20 int ra,fh;char rx; 21 inline int read(){ 22 rx=getchar(),ra=0,fh=1; 23 while(rx<'0'&&rx!='-')rx=getchar(); 24 if(rx=='-')fh=-1,rx=getchar(); 25 while(rx>='0')ra=ra*10+rx-48,rx=getchar();return ra*fh; 26 } 27 28 bool operator <(zs a,zs b){return a.t<b.t;} 29 bool cmp(int a,int b){return a>b;} 30 int main(){ 31 int n=read(),T=read();register int i,j; 32 for(i=1;i<=n;i++)a[i].t=read(),a[i].v=read(); 33 sort(a+1,a+1+n);int top=1;bool now=1,pre=0; 34 35 36 for(i=1;i<=T;i++,now^=1,pre^=1){ 37 while(top<=n&&a[top].t==i)mp[pre][++num[pre]]=a[top++].v; 38 sort(mp[pre]+1,mp[pre]+1+num[pre],cmp); 39 // for(j=1;j<=num[pre];j++)printf("%d ",mp[pre][j]);puts(""); 40 if(i==T)return printf("%d ",mp[pre][1]),0; 41 if(num[pre]&1)mp[pre][++num[pre]]=0; 42 43 for(j=1,num[now]=0;j<num[pre];j+=2)mp[now][++num[now]]=mp[pre][j]+mp[pre][j+1]; 44 } 45 }