您的位置:首页 > 其它

[bzoj4919]大根堆——set启发式合并

2018-04-02 17:01 351 查看

题目大意:

给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点。每个点有一个权值v_i。

你需要将这棵树转化成一个大根堆。确切地说,你需要选择尽可能多的节点,满足大根堆的性质:对于任意两个点i,j,如果i在树上是j的祖先,那么vi>vjvi>vj。

请计算可选的最多的点数,注意这些点不必形成这棵树的一个连通子树。

思路:

发现这其实就是在求书上的最长上升子序列,考虑我们在求树为一条链时的情况,从前往后扫,并且使用了一个单调队列,

只有当后一个节点严格大于队列中最大的元素的时候,才可以把这个元素新加进去,如果不是严格大于,我们会二分单调队列中第一个大于等于它的元素,然后将它替换,当我们把这种方法运用到树上的时候,发现同样也是成立的,只需要把一个节点的所有子节的单调队列全部合并之后,再像之前那样的做法,添加这个元素就可以了。如果不加任何优化,时间和空间上面都是过不去,所以我们使用启发式合并,并且每次合并之后删除这个set。

代码其实非常简短:

/**************************************************************
Problem: 4919
User: ylsoi
Language: C++
Result: Accepted
Time:888 ms
Memory:23124 kb
****************************************************************/

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<set>
using namespace std;
void File(){
freopen("bzoj4919.in","r",stdin);
freopen("bzoj4919.out","w",stdout);
}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define ll long long
#define inf (0x3f3f3f3f)
const int maxn=2e5+10;
int n,w[maxn],beg[maxn],cnt,fa[maxn];
struct edge{
int to;
int last;
}E[maxn*2];
void add(int u,int v){
++cnt;
E[cnt].to=v;
E[cnt].last=beg[u];
beg[u]=cnt;
}
multiset<int>s[maxn];
multiset<int>::iterator it1,it2,it3;
void merge(int x,int y){
if(s[x].size()<s[y].size())swap(s[x],s[y]);
it1=s[y].begin();it2=s[y].end();
for(;it1!=it2;++it1)s[x].insert(*it1);
s[y].clear();
}
void dfs(int u){
MREP(i,u){
int v=E[i].to;
dfs(v);
merge(u,v);
}
it3=s[u].lower_bound(w[u]);
if(it3!=s[u].end())s[u].erase(it3);
s[u].insert(w[u]);
}
int main(){
//File();
scanf("%d",&n);
REP(i,1,n){
scanf("%d%d",&w[i],&fa[i]);
if(fa[i])add(fa[i],i);
}
dfs(1);
cout<<s[1].size()<<endl;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: