uoj #139. 【UER #4】被删除的黑白树 dfs序 贪心
#139. 【UER #4】被删除的黑白树
Time Limit: 1 Sec
Memory Limit: 256 MB
题目连接
http://uoj.ac/problem/139
Description
很久很久以前,有一棵树加入了 UOJ 群。
这天,在它讨论“一棵树应该怎么旋转”的时候一不小心被删除了,变成了被删除的树。
突然间,它突然发现它失去了颜色,变成了一棵纯白的树。这让它感觉很焦躁,于是它来拜托你给自己染上一些颜色。
我们可以把它描述为一棵 n 个节点的有根树(默认树的根为 1 号节点),所有非根的度数为 1 的节点被称为叶子节点。最开始所有的节点都是白色的。
现在你需要选出一些节点并把这些节点染成黑色的。为了迎合树的审美,你的染色方案必须要满足所有叶子节点到根路径上的黑色节点个数相同。
你发现黑色节点个数越多,树就会越高兴,所以你想要知道在所有合法的染色方案中,黑色节点总个数最多是多少。
这天,在它讨论“一棵树应该怎么旋转”的时候一不小心被删除了,变成了被删除的树。
突然间,它突然发现它失去了颜色,变成了一棵纯白的树。这让它感觉很焦躁,于是它来拜托你给自己染上一些颜色。
我们可以把它描述为一棵 n 个节点的有根树(默认树的根为 1 号节点),所有非根的度数为 1 的节点被称为叶子节点。最开始所有的节点都是白色的。
现在你需要选出一些节点并把这些节点染成黑色的。为了迎合树的审美,你的染色方案必须要满足所有叶子节点到根路径上的黑色节点个数相同。
你发现黑色节点个数越多,树就会越高兴,所以你想要知道在所有合法的染色方案中,黑色节点总个数最多是多少。
Input
第一行一个正整数 n 表示树的节点个数。
接下来的 n−1 行,每行是两个整数 u,v (1≤u,v≤n,u≠v) 表示树上的一条边。
接下来的 n−1 行,每行是两个整数 u,v (1≤u,v≤n,u≠v) 表示树上的一条边。
Output
一个整数,表示在所有合法方案中黑色节点的最多数量。
Sample Input
7
1 2
1 3
2 4
2 5
3 6
3 7
Sample Output
7
HINT
题意
题解:
首先贪心一下,对于每条从叶子到根节点的黑色棋子个数,应该是最浅的叶子节点的深度
于是我们就可以一次dfs求出这个深度,然后再染色就好了
染色我比较麻烦,我是用dfs序维护的……
每次从叶子节点往上染色,先查询一下这个点的祖先已经染了多少个点,然后再慢慢跑的
代码:
#include<iostream> #include<stdio.h> #include<math.h> #include<vector> #include<algorithm> #include<cstring> using namespace std; typedef long long LL; #define LL(x) (x<<1) #define RR(x) (x<<1|1) #define MID(a,b) (a+((b-a)>>1)) #define maxn 300005 vector<int> G[maxn]; vector<int> P; int n; struct PPP { int l,d,r; }node[maxn]; struct Node { int lft,rht; LL sum,add; int mid(){return MID(lft,rht);} void fun(LL tmp) { add+=tmp; sum+=(rht-lft+1)*tmp; } }; int y[maxn]; struct Segtree { Node tree[maxn*4]; void relax(int ind) { if(tree[ind].add) { tree[LL(ind)].fun(tree[ind].add); tree[RR(ind)].fun(tree[ind].add); tree[ind].add=0; } } void build(int lft,int rht,int ind) { tree[ind].lft=lft; tree[ind].rht=rht; tree[ind].sum=0; tree[ind].add=0; if(lft==rht) tree[ind].sum=y[lft]; else { int mid=tree[ind].mid(); build(lft,mid,LL(ind)); build(mid+1,rht,RR(ind)); tree[ind].sum=tree[LL(ind)].sum+tree[RR(ind)].sum; } } void updata(int st,int ed,int ind,int add) { int lft=tree[ind].lft,rht=tree[ind].rht; if(st<=lft&&rht<=ed) tree[ind].fun(add); else { relax(ind); int mid=tree[ind].mid(); if(st<=mid) updata(st,ed,LL(ind),add); if(ed> mid) updata(st,ed,RR(ind),add); tree[ind].sum=tree[LL(ind)].sum+tree[RR(ind)].sum; } } LL query(int st,int ed,int ind) { int lft=tree[ind].lft,rht=tree[ind].rht; if(st<=lft&&rht<=ed) return tree[ind].sum; else { relax(ind); int mid=tree[ind].mid(); LL sum1=0,sum2=0; if(st<=mid) sum1=query(st,ed,LL(ind)); if(ed> mid) sum2=query(st,ed,RR(ind)); return sum1+sum2; } } }seg; int cnt = 1; int cntt = 1; int ans = 9999999; bool cmp(int a,int b) { return node[a].d<node[b].d; } void dfs3(int x,int fa,int d) { node[x].l=cntt++; node[x].d=d; for(int i=0;i<G[x].size();i++) { if(G[x][i]==fa) continue; dfs3(G[x][i],x,d+1); } node[x].r=cntt; } void dfs(int x,int pre,int add) { if(G[x].size()==1&&x!=1) { P.push_back(x); ans = min(ans,add); return; } for(int i=0;i<G[x].size();i++) { int v=G[x][i]; if(v==pre)continue; dfs(v,x,add+1); } } int Ans = 0; int vis[maxn]; void dfs2(int x,int pre,int add) { if(vis[x])return; if(add<=0)return; vis[x]=1; Ans++; seg.updata(node[x].l,node[x].r-1,1,1); for(int i=0;i<G[x].size();i++) { int v = G[x][i]; if(v==pre)continue; if(node[v].d >= node[x].d)continue; dfs2(v,x,add-1); } } int main() { //freopen("k.in","r",stdin); //freopen("2.out","w",stdout); scanf("%d",&n); if(n==1) { printf("1\n");return 0; } for(int i=1;i<n;i++) { int x,y;scanf("%d%d",&x,&y); G[x].push_back(y); G[y].push_back(x); } seg.build(1,n,1); dfs(1,-1,1); dfs3(1,-1,1); sort(P.begin(),P.end(),cmp); for(int i=0;i<P.size();i++) { int t = 0; int xx = seg.query(node[P[i]].l,node[P[i]].l,1); if(xx<0)continue; if(P[i]!=1) t = ans-xx; dfs2(P[i],-1,t); } printf("%d\n",Ans); }