【贪心 计数 倍增】bzoj4458: GTY的OJ
倍增写挂调了半个晚上
Description
身为IOI金牌的gtyzs有自己的一个OJ,名曰GOJ。GOJ上的题目可谓是高质量而又经典,他在他的OJ里面定义了一个树形的分类目录,且两个相同级别的目录是不会重叠的。比如图论的大目录下可能分为最短路,最小生成树,网络流等低一级的分类目录,这些目录下可能还有更低一级的目录,以此类推。现在gtyzs接到了一个任务,要他为SDTSC出题。他准备在自己的OJ题库中找出M道题作为SDTSC的试题,而他深知SDTSC的童鞋们个个都是神犇,所以gtyzs认为自己出的这M道题中,每道题都应该属于不少于L种分类目录;可他又怕自己的题没有人会做,所以每道题也应该属于不超过R种分类目录,并且这些分类目录应该是连续的,不能出现断层。对了,最重要的是,不存在一道题,它属于两个分类目录且这两个目录不是包含关系。(比如不存在一道题它既是一道DP题,又是一道网络流题)gtyzs怕被骂,所以他希望不存在任意的一对题目(u,v),使得u所属的分类目录与v完全相同。举例来说,gtyzs不能出两道同样的都是动态规划,背包的题,但是却可以出一道属于动态规划,背包和01背包的题和一道属于背包,01背包的题,当然也可以出一个属于图论的题和一个属于数论的题。(注意:一道题所属的分类目录不一定从根开始,如加粗部分所示)
为了让自己的题目变得更加靠谱,他给每一个分类目录都定了一个靠谱值ai,这个值可正可负。一道题的靠谱度为其从属的分类目录靠谱值的加和。我们假设动态规划的靠谱值为10,插头DP的靠谱值为-5,则一道动态规划插头DP的题的靠谱值就是5。gtyzs希望自己出的这M道题,在满足上述前提条件下,靠谱度总和最大。gtyzs当然会做啦,于是你就看到了这个题。
Input
Output
一行一个整数,表示最大的靠谱度。
Sample Input
0 1 1 2 2 3 3
2 3 4 1 2 3 4
3 3 3
Sample Output
题目分析
树上超级钢琴,配合bzoj2006: [NOI2010]超级钢琴食用更佳。
思路几乎一模一样,不过是应用了一些树与序列不同的性质去做这个事情:前缀和上树;倍增维护点$u$至$2^i$个祖先上路径$sum_i$最小值。有些±1的细节可能需要好好注意。
倍增还不够熟练
1 #include<bits/stdc++.h> 2 typedef long long ll; 3 typedef std::pair<ll, int> pr; 4 const int maxn = 500035; 5 const int maxm = 500035; 6 7 ll ans; 8 pr st[maxn][23]; 9 int fa[maxn][23],dep[maxn]; 10 int n,m,L,R,w[maxn],s[maxn]; 11 int edgeTot,head[maxn],nxt[maxm],edges[maxm]; 12 struct node 13 { 14 int u,l,r,p; 15 ll w; 16 pr query(int x, int d) 17 { 18 pr ret = pr(s[x],x); 19 for (int i=20; i>=0; i--) 20 if ((d>>i)&1) ret = std::min(ret, st[x][i]), x = fa[x][i]; 21 return ret; 22 } 23 int findFa(int x, int d) 24 { 25 for (int i=20; i>=0; i--) 26 if ((d>>i)&1) x = fa[x][i]; 27 return x; 28 } 29 void calc() 30 { 31 int fa = findFa(u, l); 32 pr tmp = query(fa, r-l); 33 w = s[u]-tmp.first; 34 p = dep[u]-dep[tmp.second]; 35 36 } 37 node(int a, int b, int c):u(a),l(b),r(c) {} 38 bool operator < (node a) const 39 { 40 return w < a.w; 41 } 42 }; 43 std::priority_queue<node> q; 44 45 int read() 46 { 47 char ch = getchar(); 48 int num = 0; 49 bool fl = 0; 50 for (; !isdigit(ch); ch=getchar()) 51 if (ch=='-') fl = 1; 52 for (; isdigit(ch); ch=getchar()) 53 num = (num<<1)+(num<<3)+ch-48; 54 if (fl) num = -num; 55 return num; 56 } 57 void addedge(int u, int v) 58 { 59 if (!u) return; 60 fa[v][0] = u, edges[++edgeTot] = v, nxt[edgeTot] = head[u], head[u] = edgeTot; 61 } 62 void init() 63 { 64 for (int i=1; i<=n; i++) 65 s[i] += s[fa[i][0]]+w[i], dep[i] = dep[fa[i][0]]+1; 66 for (int j=1; j<=20; j++) 67 for (int i=1; i<=n; i++) 68 fa[i][j] = fa[fa[i][j-1]][j-1]; 69 for (int i=0; i<=n; i++) st[i][0] = pr(s[fa[i][0]], fa[i][0]); 70 for (int j=1; j<=20; j++) 71 for (int i=0; i<=n; i++) 72 st[i][j] = std::min(st[i][j-1], st[fa[i][j-1]][j-1]); 73 } 74 int main() 75 { 76 memset(head, -1, sizeof head); 77 n = read(); 78 for (int i=1; i<=n; i++) addedge(read(), i); 79 for (int i=1; i<=n; i++) w[i] = read(); 80 m = read(), L = read(), R = read(); 81 init(); 82 for (int i=1; i<=n; i++) 83 { 84 if (dep[i] < L) continue; 85 int l = L, r = std::min(dep[i], R); 86 if (l <= r){ 87 node tmp = node(i, l, r); 88 tmp.calc(), q.push(tmp); 89 } 90 } 91 while (m--) 92 { 93 if (q.empty()) break; 94 node tt = q.top(); 95 q.pop(), ans += tt.w; 96 int u = tt.u, l = tt.l, r = tt.r, p = tt.p; 97 if (l < p){ 98 node tmp = node(u, l, p-1); 99 tmp.calc(), q.push(tmp); 100 } 101 if (r > p){ 102 node tmp = node(u, p+1, r); 103 tmp.calc(), q.push(tmp); 104 } 105 } 106 printf("%lld\n",ans); 107 return 0; 108 }
END