题意:

给你一个有n个点的树,给定根,叫你找第k大的特殊链 。
特殊的链的定义:u,v之间的路径,经过题给的根节点.

题解:(来自BC官方题解)

对于求第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(nlog2​​n)的时间复杂度内完成。再套上最外层的二分,总时间复杂度为O(nlog​3n)。

n的范围是50000,时限6s,卡常数就过去了(本行划线 由于在点分治中,复杂度中第二个logn的瓶颈在于排序。由于每次排序都是对相同的数排序,因此我们可以考虑将点分治+排序作为预处理,每次二分的时候只要做单调队列部分即可。

上述做法的总时间复杂度为O(nlog​2n)。

 #include<bits/stdc++.h>
#define F(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int N=1e5+; int T,n,m,k,g[N],nxt[N],v[N*],w[N*],ed,C[N*],hsh_ed,hsh[*N];
int sz[N],vis[N],mx[N],mi,ROOT,root,idx,ret,cnt;
vector<int>G[N*],G_rt[N]; void adg(int x,int y,int c){v[++ed]=y,w[ed]=c,nxt[ed]=g[x],g[x]=ed;}
inline void up(int &a,int b){if(a<b)a=b;} inline void add(int x,int c){while(x<=hsh_ed)C[x]+=c,x+=x&-x;}
inline int ask(int x){int an=;while(x>)an+=C[x],x-=x&-x;return an;}
inline int getid(int x){return lower_bound(hsh+,hsh++hsh_ed,x)-hsh;} void get_rt(int u,int fa,int num)
{
sz[u]=,mx[u]=;
for(int i=g[u];i;i=nxt[i])
if(v[i]!=fa&&!vis[v[i]])
get_rt(v[i],u,num),sz[u]+=sz[v[i]],up(mx[u],sz[v[i]]);
up(mx[u],num-sz[u]);
if(mx[u]<mi)mi=mx[u],root=u;
} void get_dis(int u,int fa,int dis)
{
G[cnt].push_back(dis);
for(int i=g[u];i;i=nxt[i])
if(v[i]!=fa&&!vis[v[i]])
get_dis(v[i],u,dis+w[i]);
} void init_cal(int u,int dis)
{
cnt++,get_dis(u,u,dis);
sort(G[cnt].begin(),G[cnt].end());
} void get_all(int u)
{
init_cal(u,),vis[u]=;
for(int i=g[u];i;i=nxt[i])
if(!vis[v[i]])
{
init_cal(v[i],w[i]);
mi=sz[v[i]],get_rt(v[i],v[i],sz[v[i]]);
G_rt[u].push_back(root);
get_all(root);
}
} void init_tree()
{
F(i,,n)G_rt[i].clear();
F(i,,*n)G[i].clear();
memset(vis,,sizeof(vis));
mi=n,get_rt(,,n),ROOT=root;
cnt=,get_all(ROOT);
}
//----------------以上为预处理-------- int cal(int mid)
{
int an=;
int i=,j=G[++idx].size()-;
while(i<j)
{
while(j>i&&G[idx][j]+G[idx][i]<mid)i++;
an+=j-i,j--;
}
return an;
} void work(int u,int mid)//求出所有的链
{
ret+=cal(mid),vis[u]=;
int sz=G_rt[u].size()-;
F(i,,sz)ret-=cal(mid),work(G_rt[u][i],mid);
} void dfs(int u,int fa,int dis,int mid)//将每个点过根的距离计算出来
{
hsh[++hsh_ed]=dis,hsh[++hsh_ed]=dis-mid;
for(int i=g[u];i;i=nxt[i])
if(v[i]!=fa)dfs(v[i],u,dis+w[i],mid);
} void get_ret(int u,int fa,int dis,int mid)//容斥不经过根节点的答案
{
int x=getid(dis-mid),y=getid(dis);
ret-=ask(x),add(y,);
for(int i=g[u];i;i=nxt[i])
if(v[i]!=fa)
get_ret(v[i],u,dis+w[i],mid);
add(y,-);
} int check(int mid)
{
ret=,memset(vis,,sizeof(vis));
idx=,work(ROOT,mid);
hsh_ed=,dfs(m,m,,mid);
sort(hsh+,hsh++hsh_ed);
hsh_ed=unique(hsh+,hsh++hsh_ed)-hsh-;
get_ret(m,,,mid);
if(ret>=k)return ;
return ;
} int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&k);
int maxdis=;
memset(g,,sizeof(g)),ed=;
F(i,,n-)
{
int x,y,c;
scanf("%d%d%d",&x,&y,&c);
adg(x,y,c),adg(y,x,c);
up(maxdis,c);
}
init_tree();
int l=,r=maxdis*n,ans=,mid;
while(l<=r)
{
mid=l+r>>;
if(check(mid))ans=mid,l=mid+;
else r=mid-;
}
if(!ans)puts("NO");
else printf("%d\n",ans);
}
return ;
}

hdu 5664 Lady CA and the graph(树的点分治+容斥)的更多相关文章

  1. hdu 5792(树状数组,容斥) World is Exploding

    hdu 5792 要找的无非就是一个上升的仅有两个的序列和一个下降的仅有两个的序列,按照容斥的思想,肯定就是所有的上升的乘以所有的下降的,然后再减去重复的情况. 先用树状数组求出lx[i](在第 i ...

  2. hdu 3682 10 杭州 现场 C To Be an Dream Architect 容斥 难度:0

    C - To Be an Dream Architect Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d &a ...

  3. Luogu4528 CTSC2008 图腾 树状数组、容斥

    传送门 设$f_i$表示$i$排列的数量,其中$x$表示不确定 那么$$ans=f_{1324}-f_{1432}-f_{1243}=(f_{1x2x}-f_{1423})-(f_{14xx}-f_{ ...

  4. bzoj3589 动态树 求链并 容斥

    bzoj3589 动态树 链接 bzoj 思路 求链并. 发现只有最多5条链子,可以容斥. 链交求法:链顶是两条链顶深度大的那个,链底是两个链底的\(lca\) 如果链底深度小于链顶,就说明两条链没有 ...

  5. 【HDU】HDU5664 Lady CA and the graph

    原题链接 题解 距离省选只有一周了我居然才开始数据结构康复计划 这题很简单,就是点分树,然后二分一个值,我们计算有多少条路径大于这个值 对于一个点分树上的重心,我们可以通过双指针的方法求出它子树里的路 ...

  6. HDU 4670 Cube number on a tree ( 树的点分治 )

    题意 : 给你一棵树 . 树的每一个结点都有一个权值 . 问你有多少条路径权值的乘积是一个全然立方数 . 题目中给了你 K 个素数 ( K <= 30 ) , 全部权值都能分解成这k个素数 思路 ...

  7. hdu 5792 World is Exploding 树状数组+离散化+容斥

    World is Exploding Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Other ...

  8. HDU 4336 Card Collector (期望DP+状态压缩 或者 状态压缩+容斥)

    题意:有N(1<=N<=20)张卡片,每包中含有这些卡片的概率,每包至多一张卡片,可能没有卡片.求需要买多少包才能拿到所以的N张卡片,求次数的期望. 析:期望DP,是很容易看出来的,然后由 ...

  9. HDU 6397(2018多校第8场1001) Character Encoding 容斥

    听了杜教的直播后知道了怎么做,有两种方法,一种构造函数(现在太菜了,听不懂,以后再补),一种容斥原理. 知识补充1:若x1,x2,.....xn均大于等于0,则x1+x2+...+xn=k的方案数是C ...

随机推荐

  1. maven项目添加websocket

    最近由于项目业务需求,需要用到websocket来实现即时信息的推送,学习了一下websocket,网上搜了一下学习教程,很多版本看的我云里雾里,最后选择用tomcat提供的最新版本(tomcat 启 ...

  2. python第四天

    浏览器与Server交互: import socketdef handle_request(client): buf = client.recv(1024) client.send('HTTP/1.1 ...

  3. 我是如何给discuz模板做语法高亮的

    本人一直做ASP.NET开发,近期接到任务要用Discuz开发一个社区,第一次接触PHP,PHP灵活的语法,天生的模块化,各种语法糖深深的震惊了我,我从内心深处感受到了PHP是最牛逼的语言!!! 好了 ...

  4. PHP微信支付开发之扫描支付(模式二)后如何回调

    其实在写这篇文章的时候感觉自己已经落伍了,不过笔者在百度上搜索"微信支付开发之扫描支付(模式二)后如何回调"寻找答案时,发现依旧有很多朋友没有解决这个问题,所以就把自己的解决思路分 ...

  5. [SQL基础教程] 2-1 SELECT语句基础

    [SQL基础教程] 2-1 SELECT语句基础 列的查询 Syntax SELECT<列名>,..... FROM<表名>; SELECT col_1, col_2 FROM ...

  6. NEUQ1055谭浩强C语言(第三版)习题6.11

    //迭代公式不是很理解,写出来算了.. #include <stdio.h> #include <math.h> int main() { double x0,x1; int ...

  7. Linux Tomcat 自启动

    使用chkconfig命令 修改tomcat/bin/startup.sh,在开头的地方添加如下内容 #chkconfig: #description:tomcat auto start #proce ...

  8. 4天html总结

  9. THML结构语义化之table/form

    以table/form标签语义化,做为博客首贴 应用场景1-table表格 <table> <caption> <thead> <th colspan=&qu ...

  10. 从P1到P7——我在淘宝这7年(转)

    作者: 赵超  发布时间: 2012-02-25 14:47  阅读: 114607 次  推荐: 153   [收藏] (一) 2011-12-08 [原文链接] 今天有同事恭喜我,我才知道自己在淘 ...