zoukankan      html  css  js  c++  java
  • hdu 5664 Lady CA and the graph(树的点分治+容斥)

    题意:

    给你一个有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)。

      1 #include<bits/stdc++.h>
      2 #define F(i,a,b) for(int i=a;i<=b;i++)
      3 using namespace std;
      4 const int N=1e5+7;
      5 
      6 int T,n,m,k,g[N],nxt[N],v[N*2],w[N*2],ed,C[N*2],hsh_ed,hsh[2*N];
      7 int sz[N],vis[N],mx[N],mi,ROOT,root,idx,ret,cnt;
      8 vector<int>G[N*15],G_rt[N];
      9 
     10 void adg(int x,int y,int c){v[++ed]=y,w[ed]=c,nxt[ed]=g[x],g[x]=ed;}
     11 inline void up(int &a,int b){if(a<b)a=b;}
     12 
     13 inline void add(int x,int c){while(x<=hsh_ed)C[x]+=c,x+=x&-x;}
     14 inline int ask(int x){int an=0;while(x>0)an+=C[x],x-=x&-x;return an;}
     15 inline int getid(int x){return lower_bound(hsh+1,hsh+1+hsh_ed,x)-hsh;}
     16 
     17 void get_rt(int u,int fa,int num)
     18 {
     19     sz[u]=1,mx[u]=0;
     20     for(int i=g[u];i;i=nxt[i])
     21         if(v[i]!=fa&&!vis[v[i]])
     22             get_rt(v[i],u,num),sz[u]+=sz[v[i]],up(mx[u],sz[v[i]]);
     23     up(mx[u],num-sz[u]);
     24     if(mx[u]<mi)mi=mx[u],root=u;
     25 }
     26 
     27 void get_dis(int u,int fa,int dis)
     28 {
     29     G[cnt].push_back(dis);
     30     for(int i=g[u];i;i=nxt[i])
     31         if(v[i]!=fa&&!vis[v[i]])
     32             get_dis(v[i],u,dis+w[i]);
     33 }
     34 
     35 void init_cal(int u,int dis)
     36 {
     37     cnt++,get_dis(u,u,dis);
     38     sort(G[cnt].begin(),G[cnt].end());
     39 }
     40 
     41 void get_all(int u)
     42 {
     43     init_cal(u,0),vis[u]=1;
     44     for(int i=g[u];i;i=nxt[i])
     45         if(!vis[v[i]])
     46         {
     47             init_cal(v[i],w[i]);
     48             mi=sz[v[i]],get_rt(v[i],v[i],sz[v[i]]);
     49             G_rt[u].push_back(root);
     50             get_all(root);
     51         }
     52 }
     53 
     54 void init_tree()
     55 {
     56     F(i,1,n)G_rt[i].clear();
     57     F(i,1,10*n)G[i].clear();
     58     memset(vis,0,sizeof(vis));
     59     mi=n,get_rt(1,1,n),ROOT=root;
     60     cnt=0,get_all(ROOT);
     61 }
     62 //----------------以上为预处理--------
     63 
     64 int cal(int mid)
     65 {
     66     int an=0;
     67     int i=0,j=G[++idx].size()-1;
     68     while(i<j)
     69     {
     70         while(j>i&&G[idx][j]+G[idx][i]<mid)i++;
     71         an+=j-i,j--;
     72     }
     73     return an;
     74 }
     75 
     76 void work(int u,int mid)//求出所有的链
     77 {
     78     ret+=cal(mid),vis[u]=1;
     79     int sz=G_rt[u].size()-1;
     80     F(i,0,sz)ret-=cal(mid),work(G_rt[u][i],mid);
     81 }
     82 
     83 void dfs(int u,int fa,int dis,int mid)//将每个点过根的距离计算出来
     84 {
     85     hsh[++hsh_ed]=dis,hsh[++hsh_ed]=dis-mid;
     86     for(int i=g[u];i;i=nxt[i])
     87         if(v[i]!=fa)dfs(v[i],u,dis+w[i],mid);
     88 }
     89 
     90 void get_ret(int u,int fa,int dis,int mid)//容斥不经过根节点的答案
     91 {
     92     int x=getid(dis-mid),y=getid(dis);
     93     ret-=ask(x),add(y,1);
     94     for(int i=g[u];i;i=nxt[i])
     95         if(v[i]!=fa)
     96             get_ret(v[i],u,dis+w[i],mid);
     97     add(y,-1);
     98 }
     99 
    100 int check(int mid)
    101 {
    102     ret=0,memset(vis,0,sizeof(vis));
    103     idx=0,work(ROOT,mid);
    104     hsh_ed=0,dfs(m,m,0,mid);
    105     sort(hsh+1,hsh+1+hsh_ed);
    106     hsh_ed=unique(hsh+1,hsh+1+hsh_ed)-hsh-1;
    107     get_ret(m,0,0,mid);
    108     if(ret>=k)return 1;
    109     return 0;
    110 }
    111 
    112 int main()
    113 {
    114     scanf("%d",&T);
    115     while(T--)
    116     {
    117         scanf("%d%d%d",&n,&m,&k);
    118         int maxdis=0;
    119         memset(g,0,sizeof(g)),ed=0;
    120         F(i,1,n-1)
    121         {
    122             int x,y,c;
    123             scanf("%d%d%d",&x,&y,&c);
    124             adg(x,y,c),adg(y,x,c);
    125             up(maxdis,c);
    126         }
    127         init_tree();
    128         int l=0,r=maxdis*n,ans=0,mid;
    129         while(l<=r)
    130         {
    131             mid=l+r>>1;
    132             if(check(mid))ans=mid,l=mid+1;
    133             else r=mid-1;
    134         }
    135         if(!ans)puts("NO");
    136         else printf("%d
    ",ans);
    137     }
    138     return 0;
    139 }
    View Code
  • 相关阅读:
    【转】Visual studio 快捷键大全
    C++ 中的权限控制
    论C++11 中vector的N种遍历方法
    c++ 模板仿函数初探
    OBS (open boardcast server)结构分析
    OpenCV学习笔记:opencv_core模块
    [转]C++ new操作符详解
    进程已经被attach debug,如何解除其debug权限?
    dll 在进程中怎么区分的
    树状数组学习笔记
  • 原文地址:https://www.cnblogs.com/bin-gege/p/6111483.html
Copyright © 2011-2022 走看看