[Apio2012][Treap]派遣
2016-03-31 22:01
225 查看
原题地址
题意:在树中找到一个点i,并且找到这个点子树中的一些点组成一个集合,使得集合中的所有点的c之和不超过M,且Li*集合中元素个数和最大。
现在有三种做法
很显然的贪心策略:对于每个点,我们把每个以他为代表的子树里的所有点,从小到大排好序。然后一直选小的,直到不满足条件为止。
我们从叶子到根进行合并即可。
法一:
平衡树,对于本蒟蒻来说,当然选择treap了,对于两个将被合并的treap,我们不能保证它们相对有序,因此只能使用启发式合并,即每次合并时把小的treap中的元素一个一个塞到大的里。复杂度 : 由于是把小到塞到大的里,那么那棵小的子树的大小肯定至少变为原来的两倍,因此对于每个元素,至多合并logn次,一共合并nlogn次,每次合并复杂度logn。因此全局合并的复杂度nlog方。我还小小的优化了一下:对于每个即将被合并的节点,由于合并后只会选总和不超过m的那么多个节点,所以当前被合并的节点也只有可能总和不超过m的前面的节点被合并后的节点利用,因此我们还可以在每个节点更新了答案之后,分裂一下,使它里面元素的总和恰好小于等于m。
上代码
法二:
很显然的,还可以使用左偏树秒杀,左偏树每次合并不必保证相对有序,所以合并总复杂度nlogn的。。。
但有个问题,如何求左偏树的前k个的大小和呢?
这是个极其蛋疼的问题,所以我们可以转化一下,对于每个左偏树内节点,记下它所在子树的和的大小,把当前的左偏树建成一个小根堆(大的在最上面),不断地弹出,直到元素之和小于等于m。对于每个结点,最多被弹出一次,每次logn 所以弹出的总复杂度是nlogn。。
整个程序的复杂度就是nlogn辣。
上代码
法三 莫队加权值分块,根号+根号,暴力一加一
首先我们建出dfs序,这是树形转线性的一种方法。
那么题目就变为查询很多段连续区间,求所有区间中总和加上小于等于m的点的最大个数。 (可能表达地不是很好)
那么就可以用到莫队来处理询问了。
我们发现,最多有1e5个点,因此我们可以离散化,然后权值分块,(这里的权值就是指块状每个节点相当于一个值有多少个)
每次查询根号n,移动总共n根号n,总复杂度 n 根号 n
题意:在树中找到一个点i,并且找到这个点子树中的一些点组成一个集合,使得集合中的所有点的c之和不超过M,且Li*集合中元素个数和最大。
现在有三种做法
很显然的贪心策略:对于每个点,我们把每个以他为代表的子树里的所有点,从小到大排好序。然后一直选小的,直到不满足条件为止。
我们从叶子到根进行合并即可。
法一:
平衡树,对于本蒟蒻来说,当然选择treap了,对于两个将被合并的treap,我们不能保证它们相对有序,因此只能使用启发式合并,即每次合并时把小的treap中的元素一个一个塞到大的里。复杂度 : 由于是把小到塞到大的里,那么那棵小的子树的大小肯定至少变为原来的两倍,因此对于每个元素,至多合并logn次,一共合并nlogn次,每次合并复杂度logn。因此全局合并的复杂度nlog方。我还小小的优化了一下:对于每个即将被合并的节点,由于合并后只会选总和不超过m的那么多个节点,所以当前被合并的节点也只有可能总和不超过m的前面的节点被合并后的节点利用,因此我们还可以在每个节点更新了答案之后,分裂一下,使它里面元素的总和恰好小于等于m。
上代码
#include<cstdio> #include<algorithm> #include<cstring> #include<cstdlib> #include<iostream> #include<string> #include<cmath> #include<cctype> const int N = 1e5 + 9; int n,m,c ,l ; struct Edge { int to; Edge *next; Edge () {} Edge (int to,Edge *next) : to(to),next(next) {} }ME ,*Po = ME,*head ; void AddEdge (int u,int v) { head [u] = new (Po++) Edge (v, head[u]); } struct Treap { int data, size, hr; long long sum; Treap *l, *r; Treap (int data, Treap *fl) : data(data),size(1),hr(rand()),sum(data),l(fl),r(fl) {} Treap () {} void update() { size = l -> size + r -> size + 1; sum = l -> sum + r -> sum + data; } }*Null,*root ,meme ,*pool = meme; Treap *newnode (int xxx) { return new (pool++) Treap (xxx, Null); } Treap *Merge (Treap *A, Treap *B) { if (A == Null) return B; if (B == Null) return A; if (A -> hr > B -> hr) { B -> l = Merge (A, B -> l); B -> update (); return B; } else { A -> r = Merge (A -> r, B); A -> update (); return A; } } using std :: pair; typedef pair <Treap*, Treap*> Droot; Droot Split_Rank (Treap *x, int k) { if (x == Null) return Droot (Null, Null); Droot y; if (x -> l -> size >= k) { y = Split_Rank (x -> l, k); x -> l = y . second; x -> update (); y . second = x; } else { y = Split_Rank (x -> r, k - x -> l -> size - 1); x -> r = y . first; x -> update (); y . first = x; } return y; } Droot Split_Data (Treap *x, int k) { if (x == Null) return Droot (Null, Null); Droot y; if (x -> data >= k) { y = Split_Data (x -> l, k); x -> l = y . second; x -> update (); y . second = x; } else { y = Split_Data (x -> r, k); x -> r = y . first; x -> update (); y . first = x; } return y; } // ÆäʵÄØ£¬ ÎÞÂÛÊǶѻ¹ÊÇƽºâÊ÷£¬Æô·¢Ê½¶¼ÊÇlognµÄ void Insert (Treap *&gen, Treap* xxx) { static Droot clc1; clc1 = Split_Data (gen, xxx -> data); gen = Merge (clc1 . first, Merge (xxx, clc1.second)); } int Ran (Treap *x, int& pac) { if (x == Null) return 0; int sum; if (x -> l -> sum <= pac) pac -= x -> l -> sum, sum = x -> l -> size; else sum = Ran (x -> l, pac); if (pac >= x -> data) pac -= x -> data, ++sum; if (pac >= x -> data) sum += Ran (x -> r, pac); return sum; } long long ans; int jl; Treap *u,*v; Droot clc1; void Dfs_Merge (int x) { if(head[x]) { for (Edge *now = head[x]; now; now = now -> next) { Dfs_Merge (now -> to); u = root[x]; v = root[now -> to]; // ¿¼ÂǺϲ¢uºÍv... if (u -> size > v -> size) std :: swap (u, v); // È·±£uС£¬v´ó while (u -> size) { clc1 = Split_Rank (u, 1); u = clc1 . second; Insert (v, clc1.first); } root[x] = v; } Insert (root[x],newnode(c[x])); int M = m; int ran = Ran (root[x], M); root[x] = Split_Rank (root[x],ran) . first; ans = std :: max (ans, 1ll * ran * l[x]); } else { root [x] = newnode (c[x]); ans = std :: max (ans, 1ll * l[x]); } } void Init () { Null = new Treap (); Null -> size = 0; int b; scanf ("%d%d",&n,&m); for (int i=1;i<=n;++i) { scanf ("%d%d%d",&b,c+i,l+i); AddEdge (b,i); root[i] = Null; } } int main () { Init (); Dfs_Merge (1); printf("%lld\n",ans); return 0; }
法二:
很显然的,还可以使用左偏树秒杀,左偏树每次合并不必保证相对有序,所以合并总复杂度nlogn的。。。
但有个问题,如何求左偏树的前k个的大小和呢?
这是个极其蛋疼的问题,所以我们可以转化一下,对于每个左偏树内节点,记下它所在子树的和的大小,把当前的左偏树建成一个小根堆(大的在最上面),不断地弹出,直到元素之和小于等于m。对于每个结点,最多被弹出一次,每次logn 所以弹出的总复杂度是nlogn。。
整个程序的复杂度就是nlogn辣。
上代码
#include<cstdio> #include<cstring> #include<string> #include<cstdlib> #include<iostream> #include<algorithm> #include<cctype> #include<cmath> const int N = 1e5 + 9; int n, m, c , l ; long long ans; struct Edge { int to; Edge *next; Edge () {} Edge (int to, Edge *next) : to(to),next(next) {} }*head ,Me ,*Po = Me; void AddEdge (int u, int v) { head[u] = new (Po++) Edge (v, head[u]); } struct LT { int size, dist, data; long long sum; LT *l, *r; LT () {} LT (int data, LT *fl) : size(1),dist(0),data(data),sum(data),l(fl),r(fl) {} void update () { sum = l -> sum + r -> sum + data; dist = r -> dist + 1; size = l -> size + r -> size + 1; } }*Null, *root , meme , *pool = meme; LT *newnode (int xxx) { return new (pool++) LT (xxx, Null); } LT *Merge (LT *A, LT *B) { if (A == Null) return B; if (B == Null) return A; if (A -> data < B -> data) std :: swap (A, B); A -> r = Merge (A -> r, B); if (A -> r -> dist > A -> l -> dist) std :: swap (A -> l, A -> r); A -> update (); return A; } void Dfs_Merge (int x) { for (Edge *now = head[x]; now; now = now -> next) { Dfs_Merge (now -> to); root[x] = Merge (root[x], root[now -> to]); } root[x] = Merge (root[x], newnode(c[x])); while (root[x] -> sum > m) { root[x] = Merge (root[x] -> l,root[x] -> r); } ans = std :: max (ans , 1ll * root[x] -> size * l[x]); } void Init () { Null = new LT(); Null -> size = Null -> dist = Null -> sum = Null -> data = 0; scanf ("%d%d",&n,&m); int ooo; for (int i = 1; i<= n; ++ i) scanf ("%d%d%d",&ooo,&c[i],&l[i]), AddEdge (ooo, i) , root[i] = Null; } int main () { Init (); Dfs_Merge (1); printf ("%lld\n",ans); return 0; }
法三 莫队加权值分块,根号+根号,暴力一加一
首先我们建出dfs序,这是树形转线性的一种方法。
那么题目就变为查询很多段连续区间,求所有区间中总和加上小于等于m的点的最大个数。 (可能表达地不是很好)
那么就可以用到莫队来处理询问了。
我们发现,最多有1e5个点,因此我们可以离散化,然后权值分块,(这里的权值就是指块状每个节点相当于一个值有多少个)
每次查询根号n,移动总共n根号n,总复杂度 n 根号 n
相关文章推荐
- 区块链技术资料
- poj 1001 Exponentiation(大数)
- 用布尔型输出从0到一个数中间被三整除的数
- PHP的配置
- C++primer 练习10.16
- hdu 5652 India and China Origins(二分+bfs || 并查集)BestCoder Round #77 (div.2)
- 160331
- struts2工作流程
- 蘑菇街在线笔试题
- 软考中高项学员:2016年3月28日作业
- 【bzoj2022】弹飞绵羊
- leetcode_154 Find Minimum in Rotated Sorted Array II
- jquery中生成一个带cha的tag
- [C#]UDP通讯
- ZOJ 2736 Daffodil number
- c++中生成n个m位的随机不重复字符串的一种方法(字母+数字)
- Android 的 SDK Manager 无法启动 闪退解决方法
- 解决Attribute "rippleColor" has already been defined的问题
- Code resource ✵1
- 关于HashMap的ContainsKey()