Hdu 5664 Lady CA and the graph(有n个点的树,给定根,叫你找第k大的特殊链)
来源:互联网 发布:沈阳化工大学淘宝地址 编辑:程序博客网 时间:2024/06/05 23:06
传送门:Hdu 5664 Lady CA and the graph
题意:
给你一个有n个点的树,给定根,叫你找第k大的特殊链
特殊的链的定义:u,v之间的路径,且lca(u,v)!=u&&lca(u,v)!=v,且路径第k大
思路:转自http://bestcoder.split.hdu.edu.cn/solutions.php?page=2
对于求第k大的问题,我们可以通过在外层套一个二分,将其转化为求不小于mid的有多少个的问题。接下来我们讨论如何求树上有多少条折链的长度不小于k。
我们考虑常规的点分治(对于重心,求出其到其他点的距离,排序+单调队列),时间复杂度为O(nlog^2n),但是这只能求出普通链的数量。
我们考虑将不属于折链的链容斥掉。也即,我们需要求出有多少条长度不小于mid的链,满足一端是另一端的祖先。设有一条连接u,v的链,u是v的祖先。
我们设
d[i] 为从根到i的链的长度,然后枚举v,然后计算在从根到v的链上,有多少个点i满足d[v]−dist[i]≥mid 我们可以按照dfs序访问各结点,动态维护从根到其的链上各d值构成的权值树状数组,就能够计算这种链的数量。时间复杂度为O(nlogn)。 因此求长度不小于mid的折链数量可以在O(nlog2n)的时间复杂度内完成。再套上最外层的二分,总时间复杂度为O(nlog3n)。
n的范围是50000,时限6s,卡常数就过去了(本行划线 由于在点分治中,复杂度中第二个logn的瓶颈在于排序。由于每次排序都是对相同的数排序,因此我们可以考虑将点分治+排序作为预处理,每次二分的时候只要做单调队列部分即可。
上述做法的总时间复杂度为O(nlog2n)。
#include<bits/stdc++.h>using namespace std;const int maxn=1e5+10;int tot,head[maxn],size[maxn],root,f[maxn],id,Count,Ans,sz;//f[i]表示i这个子树的重心bool Del[maxn];int n,t[maxn],cnt,Root;;vector<int>G[maxn*10],que[maxn];struct Edge{ int to,next,cost;}e[maxn];void init(){ tot=0; memset(head,-1,sizeof(head));}void addedge(int u,int v,int w){ e[tot].to=v; e[tot].next=head[u]; e[tot].cost=w; head[u]=tot++;}void getroot(int u,int pre){ size[u]=1,f[u]=0; for(int i=head[u];i!=-1;i=e[i].next){ int v=e[i].to; if(v!=pre&&!Del[v]){ getroot(v,u); size[u]+=size[v]; f[u]=max(f[u],size[v]); } } f[u]=max(f[u],Count-size[u]); if(f[u]<f[root]) root=u;}void getdeep(int u,int pre,int dep){ G[cnt].push_back(dep); size[u]=1; for(int i=head[u];i!=-1;i=e[i].next){ int v=e[i].to; if(v!=pre&&!Del[v]){ getdeep(v,u,dep+e[i].cost); size[u]+=size[v]; } }}long long Cal(int u,int dep){ ++cnt; getdeep(u,0,dep); sort(G[cnt].begin(),G[cnt].end());}void Work(int u){ Cal(u,0);//表示链的两边的能为tmp Del[u]=true; for(int i=head[u];i!=-1;i=e[i].next){ int v=e[i].to; if(!Del[v]){ Cal(v,e[i].cost); f[0]=Count=size[v];//表示这个子树的根 getroot(v,root=0); que[u].push_back(root); Work(root); } }}void Init(){ for(int i=1;i<=n;i++) que[i].clear(); for(int i=1;i<=10*n;i++) G[i].clear(); memset(Del,false,sizeof(Del)); f[0]=Count=n; getroot(1,root=0),cnt=0; Root=root; Work(root);}//相当于把排序去掉long long cal(int mid){ long long ret=0; ++id; int num=G[id].size()-1,r=num; for(int i=0;i<num;i++){ r=max(i+1,r); while(r>i+1&&G[id][r-1]+G[id][i]>=mid) r--; if(G[id][r]+G[id][i]<mid) continue; ret+=(num-r+1); } return ret;}void work(int u,int mid){ Ans+=cal(mid);//表示链的两边的能为tmp int x=0; Del[u]=true; for(int i=head[u];i!=-1;i=e[i].next){ int v=e[i].to; if(!Del[v]){ Ans-=cal(mid); work(que[u][x++],mid); } }}int C[maxn],m,TOT;void add(int x,int num){ while(x<=TOT) C[x]+=num,x+=(x&-x);}int sum(int x){ int ret=0; while(x>0) ret+=C[x],x-=(x&-x); return ret;}void dfs(int u,int dep,int pre,int mid){ t[++sz]=dep; t[++sz]=dep-mid; for(int i=head[u];i!=-1;i=e[i].next){ int v=e[i].to; if(v!=pre) dfs(v,dep+e[i].cost,u,mid); }}void Dfs(int u,int dep,int pre,int mid){ int num1=lower_bound(t+1,t+TOT+1,dep-mid)-t,num2=lower_bound(t+1,t+TOT+1,dep)-t; Ans-=sum(num1); add(num2,1); for(int i=head[u];i!=-1;i=e[i].next){ int v=e[i].to; if(v!=pre) Dfs(v,dep+e[i].cost,u,mid); } add(num2,-1);}bool check(int mid,int k){ Ans=0,memset(Del,false,sizeof(Del)); id=0,work(Root,mid); memset(C,0,sizeof(C)); sz=0,dfs(m,0,0,mid); sort(t+1,t+sz+1); TOT=unique(t+1,t+sz+1)-t-1; Dfs(m,0,0,mid); if(Ans>=k) return true; return false;}int main(){ int _,k; scanf("%d",&_); while(_--){ scanf("%d%d%d",&n,&m,&k); int maxv=0,u,v,w; init(); for(int i=1;i<n;++i){ scanf("%d%d%d",&u,&v,&w); addedge(u,v,w),addedge(v,u,w); maxv=max(maxv,w); } Init(); //check(18,1); int low=0,high=maxv*n,ans=0; while(high-low>=0){ int mid=(low+high)>>1; if(check(mid,k)) low=mid+1,ans=mid; else high=mid-1; } if(ans==0) printf("NO\n"); else printf("%d\n",ans); } return 0;}
- Hdu 5664 Lady CA and the graph(有n个点的树,给定根,叫你找第k大的特殊链)
- HDU 5664 Lady CA and the graph 二分,树分治
- hdu 5664 Lady CA and the graph (树分治,树状数组)
- 有一个整数数组,请你根据快速排序的思路,找出数组中第K大的数。 给定一个整数数组a,同时给定它的大小n和要找的K(K在1到n之间),请返回第K大的数,保证答案存在。
- 找n个点相距最远的k个
- 找包含N个元素的数组里第K大的元素(引申:快速排序、找中位数、找前K大的元素)的时间复杂度
- Hdu 3726 Graph and Queries(删边,查询第k大,修改点值)
- 找n个数字中第k小的元素
- hdu 5656 CA Loves GCD(n个任选k个的最大公约数和)
- 九章算法面试题64 找第k大的特殊数
- 找第k大的数
- 找第K大的数
- 找第K大的数
- 找第k大的数
- 给定一个数组,找到第k到m(0<k<=m<=n)大的数
- [算法] 已知在平面坐标系内有N个点,求离开给定坐标距离最近的10个点
- leetcode_60. Permutation Sequence 找n的全排列中的第k个序列
- m维空间里n个点每点最近的第k个点的距离
- 使用HaProxy实现Thrift服务端的负载均衡
- iOS应用程序内购/内付费(一)
- LTSpline
- Java反射机制
- 在 C++ 中使用QML对象
- Hdu 5664 Lady CA and the graph(有n个点的树,给定根,叫你找第k大的特殊链)
- 网速魔法师 v1.8 官方安装版
- git 使用教程,常用命令
- 说说这阵子遇到的烦人bug
- zabbix3.0.4 邮件告警详细配置
- JS动画
- Swift中@IBDesignable/@IBInspectable的使用
- Maven学习总结(24)——Maven版本管理详解
- 部署NGINX+PHP+Thinkphp