CF 372D Choosing Subtree is Fun
2013-12-14 02:14
447 查看
题目链接:http://codeforces.com/problemset/problem/372/D
题意:给出一棵树。找到一个树的子集。在该子集包含节点数不超过K的情况下,使得该子集包含的连续编号的节点数最大?
思路: 首先,DFS一次,得到一个DFS序,就是每个节点是第几个被DFS到的,这个程序中用cnt[u]表示,以及记录在DFS序中某几次遍历的编号是谁,这个用id[i]表示。id[cnt[u]]=u。然后二分答案。对于二分的值M,保证每次插入到set中的节点数都是连续的M个,比如第一次一次性插入M-1个点,然后每次插入一个,然后到下一个时删除已经插入到set中的最前面的那个。插入删除时更新子集中的点的个数。小于等于K则该二分值可达到。下面说在插入删除时如何更新答案。插入节点时插入的是每个节点的cnt值,即DFS序中的编号。每次插入时找到该编号的前一个编号的点以及后一个编号的点,不妨设为pre和next。此次插入的点需要新增的点数为dep[u]-dep[LCA(pre,u)]-dep[LCA(next,u)]+dep[LCA(pre,next)]。
题意:给出一棵树。找到一个树的子集。在该子集包含节点数不超过K的情况下,使得该子集包含的连续编号的节点数最大?
思路: 首先,DFS一次,得到一个DFS序,就是每个节点是第几个被DFS到的,这个程序中用cnt[u]表示,以及记录在DFS序中某几次遍历的编号是谁,这个用id[i]表示。id[cnt[u]]=u。然后二分答案。对于二分的值M,保证每次插入到set中的节点数都是连续的M个,比如第一次一次性插入M-1个点,然后每次插入一个,然后到下一个时删除已经插入到set中的最前面的那个。插入删除时更新子集中的点的个数。小于等于K则该二分值可达到。下面说在插入删除时如何更新答案。插入节点时插入的是每个节点的cnt值,即DFS序中的编号。每次插入时找到该编号的前一个编号的点以及后一个编号的点,不妨设为pre和next。此次插入的点需要新增的点数为dep[u]-dep[LCA(pre,u)]-dep[LCA(next,u)]+dep[LCA(pre,next)]。
vector<int> g ; int n,K; int f [20],dep ,id ,cnt ,tot; set<int> S; void DFS(int u,int pre) { f[u][0]=pre; id[cnt[u]=++tot]=u; int i,v; FOR0(i,SZ(g[u])) { v=g[u][i]; if(v!=pre) dep[v]=dep[u]+1,DFS(v,u); } } int LCA(int u,int v) { if(dep[u]>dep[v]) swap(u,v); int k=dep[v]-dep[u],i; FOR0(i,20) if(k&(1<<i)) v=f[v][i]; if(v==u) return u; for(i=19;i>=0;i--) if(f[u][i]&&f[v][i]&&f[u][i]!=f[v][i]) { u=f[u][i]; v=f[v][i]; } return f[u][0]; } int sum; void modify(int op,int u) { set<int>::iterator it=S.lower_bound(cnt[u]); if(*it==cnt[u]) { S.erase(cnt[u]); it=S.lower_bound(cnt[u]); } if(!S.size()) { if(op==1) S.insert(cnt[u]); return; } int pre,next; if(it==S.begin()) pre=id[*(--S.end())]; else pre=id[*(--it)],it++; if(it==S.end()) next=id[*S.begin()]; else next=id[*it]; int temp=dep[u]-dep[LCA(pre,u)]-dep[LCA(next,u)]+dep[LCA(pre,next)]; if(op==1) sum+=temp,S.insert(cnt[u]); else sum-=temp; } int OK(int M) { sum=0; S.clear(); int i; FOR1(i,M-1) modify(1,i); for(i=M;i<=n;i++) { modify(1,i); if(sum+1<=K) return 1; modify(0,i+1-M); } return 0; } int main() { RD(n,K); int i,j; FOR1(i,n-1) { int u,v; RD(u,v); g[u].pb(v); g[v].pb(u); } DFS(1,0); for(i=1;i<20;i++) FOR1(j,n) f[j][i]=f[f[j][i-1]][i-1]; int low=1,high=n,M,ans; while(low<=high) { M=(low+high)>>1; if(OK(M)) ans=M,low=M+1; else high=M-1; } PR(ans); }
相关文章推荐
- ration rose2003 安装方法
- (leetcode)QUESTION 1
- ColorMatrix
- for循环的简介及break和continue的区别
- 黑马程序员 正则表达式
- C++函数模板声明和定义分离的方法
- 【cpp】reference & copy constructor
- LVS DR模型详解
- CSS float浮动的深入研究、详解及拓展
- 黑马程序员 网络编程
- 查询某个用户所有的表结构语句
- 命令创建表空间
- 贪心区间调度 Greedy interval scheduling problem
- 工作之余的胡思乱想
- LeetCode Reorder List
- vi c++ ide环境搭建
- 二叉树基于遍历的进一步操作c++实现
- UBOOT 学习心得(UBOOT流程分析)
- jQuery源码分析-04 选择器-Sizzle-块分割器chunker(有图有真相)
- 黑马程序员 接口和内部类