Hdu 5664 Lady CA and the graph(有n个点的树,给定根,叫你找第k大的特殊链)

原创 2016年08月30日 14:35: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(nlog2​​n)的时间复杂度内完成。再套上最外层的二分,总时间复杂度为O(nlog​3n)。

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

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

#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;
}
版权声明: ˋ( ° ▽、° )  ̄へ ̄

Bzoj 3672 购票(树分治+凸壳维护)

题意:给出一棵有根树(1为根),边有长度。每个点u有三个属性(len[u],p[u],q[u]),每次u可以转移到u的某个祖先节点v(v满足dist(u,v)...

Kattis yatp(斜率优化+树分治)

题意: 2e5个点的无根树,每个点有点权,每条边有边权,定义一条简单路径的花费=这条路径两个端点点权的乘积+边权和, (一条简单路径可以包含一个点,这样花费是该点权的平方),最后问从每个点...

hdu 5656 CA Loves GCD(n个任选k个的最大公约数和)

CA Loves GCD    Accepts: 64    Submissions: 535  Time Limit: 6000/3000 MS (Java/Others)    Memory L...

【HDU】4984 Goffi and Graph 最大生成树

传送门:【HDU】4984 Goffi and Graph

HDU 5409 CRB and Graph Tarjan求桥,点双联通+思维好题

Problem Description A connected, undirected graph of N vertices and M edges is given to CRB. A pai...

Hdu 3726 Graph and Queries(并查集+平衡树+启发式合并)

题目链接 Graph and Queries Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (J...

HDU 5412 CRB and Queries(区间第K大 树套树 按值建树)

HDU 5412 CRB and Queries(区间第K大 树套树 按值建树)

hdu5424 Rikka with Graph II(n个点n条边的图判哈密顿通路)

Link:http://acm.hdu.edu.cn/showproblem.php?pid=5424 Rikka with Graph II Time Limit: 2000/1...

HDU 4602 Partition 组成n的方案中k有几个 (好题!!!!找规律题+快速幂)

Define f(n) as the number of ways to perform n in format of the sum of some positive integers. For i...

hdu 5412 CRB and Queries(动态区间第k大值,区间能修改)(整体二分,树状数组套平衡树,线段树套treap)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5412 解题思路: 官方题解: In this problem, we can u...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Hdu 5664 Lady CA and the graph(有n个点的树,给定根,叫你找第k大的特殊链)
举报原因:
原因补充:

(最多只允许输入30个字)