【LOJ】#2268. 「SDOI2017」苹果树
2018-10-02 16:26
239 查看
题解
显然权值都是正的,我们最深的那个点一定延伸到了某个叶子
我们抛去这条链之外再选K个点即可
如果直接对一棵树选K个点,满足这样的依赖关系,可以通过一个后序遍历的顺序做出来
转移方法是
\(dp[i][j] = dp[i - 1][k] + (j - k) * v\)
或者
\(dp[i][j] = dp[i - siz[u]][j]\)
代表这个点选或者不选
我们把每个点拆成1和a[i] - 1两个点,然后做两次儿子遍历顺序恰好相反的dp
我们枚举一个叶子的时候,在这个点右侧这两个后序遍历重合的地方只有这个叶子到根所有点,这也是我们要必选的点
然后我们用两个遍历中这个点左侧的点集,枚举每个点集选几个,来更新答案即可
代码
#include <bits/stdc++.h> #define enter putchar('\n') #define space putchar(' ') #define fi first #define se second #define MAXN 40005 //#define ivorysi #define pii pair<int,int> #define pb push_back using namespace std; typedef long long int64; template<class T> void read(T &res) { res = 0;char c = getchar();T f = 1; while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); } while(c >= '0' && c <= '9') { res = res * 10 + c - '0'; c = getchar(); } res *= f; } template<class T> void out(T x) { if(x < 0) {putchar('-');x = -x;} if(x >= 10) out(x / 10); putchar('0' + x % 10); } struct node { int to,next; }E[MAXN * 2]; int64 tot; int N,K,head[MAXN],sumE,fa[MAXN],Ncnt,v[MAXN],a[MAXN],sumv[MAXN]; int siz[MAXN],dep[MAXN],LA[MAXN],posA[MAXN],LB[MAXN],posB[MAXN],idx; int f[51000005],g[51000005],Q1[500005],Q2[500005],ql,qr; bool lef[MAXN]; void add(int u,int v) { E[++sumE].to = v; E[sumE].next = head[u]; head[u] = sumE; } void dfsa(int u) { siz[u] = 1;dep[u] = dep[fa[u]] + 1;sumv[u] = sumv[fa[u]] + v[u]; for(int i = head[u] ; i ; i = E[i].next) { int v = E[i].to; dfsa(v);siz[u] += siz[v]; } LA[++idx] = u;posA[u] = idx; } void dfsb(int u) { vector<int> son;son.clear(); for(int i = head[u] ; i ; i = E[i].next) { int v = E[i].to;son.pb(v); } reverse(son.begin(),son.end()); for(int i = 0 ; i < son.size() ; ++i) dfsb(son[i]); LB[++idx] = u;posB[u] = idx; } void DP(int *L,int *p,int *f) { for(int i = 1 ; i <= Ncnt ; ++i) { int u = L[i]; int *f1 = f + (i - 1) * (K + 1),*f2 = f + i * (K + 1); memcpy(f2,f + (p[u] - siz[u]) * (K + 1),sizeof(int) * (K + 1)); if(u <= N) { for(int j = 1 ; j <= K ; ++j) f2[j] = max(f2[j],f1[j - 1] + v[u]); } else { Q1[ql = qr = 1] = 0;Q2[1] = 0; for(int j = 1 ; j <= K ; ++j) { while(ql <= qr && j - Q1[ql] > a[u]) ++ql; if(ql <= qr) f2[j] = max(f2[j],Q2[ql] + j * v[u]); while(ql <= qr && f1[j] - j * v[u] >= Q2[qr]) --qr; Q2[++qr] = f1[j] - j * v[u];Q1[qr] = j; } } } } void Init() { memset(head,0,sizeof(head)); memset(lef,0,sizeof(lef)); sumE = 0;tot = 0; memset(f,0,sizeof(int) * Ncnt * (K + 1)); memset(g,0,sizeof(int) * Ncnt * (K + 1)); read(N);read(K); Ncnt = N; for(int i = 1 ; i <= N ; ++i) { read(fa[i]);read(a[i]);read(v[i]); if(fa[i] != 0) add(fa[i],i); lef[fa[i]] = 1; tot += a[i]; if(a[i] > 1) { add(i,++Ncnt);fa[Ncnt] = i;v[Ncnt] = v[i];a[Ncnt] = a[i] - 1;a[i] = 1; } } idx = 0;dfsa(1); idx = 0;dfsb(1); } void Solve() { DP(LA,posA,f); DP(LB,posB,g); int ans = 0; for(int u = 1 ; u <= N ; ++u) { if(!lef[u]) { int *df = f + (K + 1) * (posA[u] - 1),*dg = g + (K + 1) * (posB[u] - siz[u]); int64 cur = min((int64)K,tot - dep[u]); for(int j = 0 ; j <= cur ; ++j) { ans = max(ans,df[j] + dg[cur - j] + sumv[u]); } } } out(ans);enter; } int main() { #ifdef ivorysi freopen("f1.in","r",stdin); #endif int T; read(T); while(T--) { Init(); Solve(); } return 0; }
感觉一看到SDOI R2自动降智
这么难的省选题要是我去考根本考不动啊
相关文章推荐
- LOJ 「SDOI2017」新生舞会(二分 + 分数规划+ 费用流)
- LOj 2000「SDOI2017」数字表格 (莫比乌斯反演)
- 【LOJ】#2269. 「SDOI2017」切树游戏
- 【LOJ】#2270. 「SDOI2017」天才黑客
- LOJ刷题记录:2000-2005(SDOI2017)
- 苹果树
- 成长的寓言:做一棵永远成长的苹果树
- 做一课永远成长的苹果树!
- loj 1109
- loj 1300( 边双联通 + 判奇圈 )
- loj 1017(dp)
- loj 1316(spfa预处理+状压dp)
- loj 1210 (求最少的加边数使得图变成强连通)
- loj 1221(spfa判正环)
- loj 1155(最大流)
- loj 1412(树上最长直径的应用)
- HDU 2268 How To Use The Car (数学题)
- [LOJ 1030] Discovering Gold
- 3757: 苹果树 树上莫队 位运算技巧
- ural 1018 二*苹果树