CF293E Close Vertices 点分治+树状数组

题目大意:
给你一棵有 n ( n <= 10 5 ) 个点的树,每条边有边权 w i ( 0 <= w i <= 10 4 ) ,定义两个点 ( x , y ) 的距离为简单路径上的边数,权重为简单路径上的权值和。求有多少对 ( x , y ) ,其中 x y ,距离小于等于 l ( l <= n ) ,权重小于等于 w ( 0 <= w <= 10 9 )

分析:
两点之间的距离和权重显然可以想到点分治了。考虑一棵子树对于前面的子树的贡献。先只考虑一维距离,我们可以把当前子树节点的到当前根的距离从大到小排序,把前面的子树的节点从小到大排序,这时候只需要分别使用一个指针就可以了。当一个指针右移使距离变小时,另一个指针右移增大,然后统计答案即可。复杂度是与节点数线性相关的。

对于两维的话,第一维还是按照上述方法,而第二个指针右移时,把这些节点的第二维丢到一个树状数组里,然后就可以统计答案了。由于距离最大只有 n 1 ,而权值能达到 10 9 ,所以第一维应该设为权值,第二维应该设为距离。

代码:

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#define LL long long

const int maxn=1e5+7;

using namespace std;

int n,l,d,x,w,cnt,root,sum,head,tail;
int t[maxn];
LL ans;
int size[maxn],vis[maxn],dis[maxn],len[maxn],f[maxn];
int ls[maxn];

struct edge{
    int y,w,next;
}g[maxn*2];

struct rec{
    int l,d;
}h[maxn];

queue <int> q;

void add(int x,int y,int w)
{
    g[++cnt]=(edge){y,w,ls[x]};
    ls[x]=cnt;
}

bool cmp1(rec x,rec y)
{
    return x.d<y.d;
}

bool cmp2(rec x,rec y)
{
    return x.d>y.d;
}

void findroot(int x,int fa)
{
    size[x]=1;
    f[x]=0;
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if ((y==fa) || (vis[y])) continue;
        findroot(y,x);
        size[x]+=size[y];
        f[x]=max(f[x],size[y]);
    }
    f[x]=max(f[x],sum-size[x]);
    if ((f[x]<f[root]) || (!root)) root=x;
}

int lowbit(int x)
{
    return x&(-x);
}

void updata(int x,int k)
{
    while (x<=n)
    {
        t[x]+=k;
        x+=lowbit(x);
    }
}

int getsum(int x)
{
    int sum=0;
    while (x>0)
    {
        sum+=t[x];
        x-=lowbit(x);
    }
    return sum;
}

void dfs(int x,int fa)
{
    size[x]=1;
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if ((y==fa) || (vis[y])) continue;
        len[y]=len[x]+1;
        dis[y]=dis[x]+g[i].w;
        h[++tail]=(rec){len[y],dis[y]};
        dfs(y,x);
        size[x]+=size[y];
        if (x==root)
        {
            sort(h+1,h+head+1,cmp1);
            sort(h+head+1,h+tail+1,cmp2);
            int k=1;
            for (int j=head+1;j<=tail;j++)
            {
                while ((h[k].d+h[j].d<=d) && (k<=head))
                {
                    updata(h[k].l+1,1);
                    k++;
                }
                ans+=(LL)getsum(l-h[j].l+1);
            }
            for (int g=1;g<k;g++) updata(h[g].l+1,-1);
            head=tail;
            q.push(y);
        }
    }
}

int main()
{
    scanf("%d%d%d",&n,&l,&d);   
    for (int i=2;i<=n;i++)
    {
        scanf("%d%d",&x,&w);
        add(i,x,w);
        add(x,i,w);
    }   
    q.push(1);
    size[1]=n;  
    while (!q.empty())
    {
        int x=q.front();
        q.pop();
        sum=size[x];
        root=0;
        findroot(x,0);
        len[root]=dis[root]=0;
        head=1; tail=1;
        h[1]=(rec){0,0};
        dfs(root,0);
        vis[root]=1;
    }
    printf("%lld",ans);
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/81365474
今日推荐