AcWing 252. 树 (点分治)打卡

题目:https://www.acwing.com/problem/content/254/

题意:求一棵树上,路径<=k的有多少条

思路:点分治,我们用两个指针算solve函数,首先对算出来的路径每个排个序,我们就保证有单调性,然后l从前往后,r从后往前,如果l+r<=m  那么(l,r-1) (l,r-2)...都是可以的,直接加上总数即可,如果不满足 r--,满足l++,这个自己写个例子就能明白的

#include<bits/stdc++.h>
#define maxn 100005
#define mod 1000000007
using namespace std;
typedef long long ll;
ll da;
vector<pair<ll,ll> > mp[maxn];//存下图 
bool vis[maxn];//标记曾经使用过的重心 
ll maxsize[maxn],dis[maxn],d[maxn];//maxsize 当前节点的最大子树 
ll siz[maxn],e[maxn];// dis 到重心的距离  d 出现过的距离 
ll n,m,rt,sum,qe;  // siz 当前节点的子树个数  e 出现的距离  rt代表当前重心 
void find(ll x,ll f){//找出重心 
    siz[x]=1;
    maxsize[x]=0;
    for(int i=0;i<mp[x].size();i++){
        pair<ll,ll> q=mp[x][i];
        if(q.first==f||vis[q.first]) continue;//vis数组标记曾经使用过的重心 
        find(q.first,x);
        siz[x]+=siz[q.first];
        maxsize[x]=max(maxsize[x],siz[q.first]); 
    } 
    maxsize[x]=max(maxsize[x],sum-siz[x]);//节点总数减去当前的子树数=以当前节点为根的父亲点子树数 
    if(maxsize[x]<maxsize[rt]){
        rt=x;
    } 
}
void get_dis(ll x,ll f,ll len){
    if(len<=1e7){
        e[++qe]=len;
    } 
    for(int i=0;i<mp[x].size();i++){
        pair<ll,ll> q=mp[x][i];
        if(q.first==f||vis[q.first]) continue;
        dis[q.first]=dis[x]+len;
        get_dis(q.first,x,len+q.second);
    }    
}
ll solve(ll x,ll len){
    ll ee=0;
    qe=0;
    dis[x]=len;
    get_dis(x,0,len);
    sort(e+1,e+qe+1);
    ll l=1,r=qe;
    while(l<r){
        if(e[l]+e[r]<=m){
            ee+=r-l;
            l++;
        }
        else{
            r--;
        }
    }
    return ee;
}
void divide(ll x){
    da+=solve(x,0);
    vis[x]=1;
    for(int i=0;i<mp[x].size();i++){
        pair<ll,ll> q=mp[x][i];
        if(vis[q.first]) continue;
        da-=solve(q.first,q.second);
        sum=siz[q.first];
        rt=0;
        maxsize[rt]=mod;
        find(q.first,x);
        divide(rt);
    }
}
void init(){
    da=0;
    for(int i=0;i<=n;i++) mp[i].clear();
    memset(maxsize,0,sizeof(maxsize));
    memset(vis,0,sizeof(vis)); 
} 
int main(){
    while(cin>>n>>m)
    {
        if(n==0&&m==0) break;
        ll a,b,c;
        init();
        for(int i=0;i<n-1;i++){
            cin>>a>>b>>c;
            a++;
            b++;
            mp[a].push_back(make_pair(b,c));
            mp[b].push_back(make_pair(a,c));
        }
        sum=n;//当前节点数 
        rt=0;
        maxsize[0]=mod;//置初值 
        find(1,0);
        divide(rt);
        printf("%lld\n",da);
    }
} 

猜你喜欢

转载自www.cnblogs.com/Lis-/p/11347794.html
今日推荐