[poj1741]tree——点分治模板 大佬们的博客 Some Links

题目大意:

给定一棵树,求其中路径上权值总和小于k的路径条数。

思路:

点分治模板,明明在前几个月我是学过的,但是好像几个月没有碰之后就像没学一样
点分治简单点讲就是为了处理树上的路径问题而通过分治来每一个点单独处理通过这个点的路径。
所谓分治就是在这个分治点计算完它的贡献之后,对于它的每一颗子树进行分治。我们对于这个分治点处理了所有通过这个分治点的路径,所以对于它的每一颗子树,我们只要单独处理这颗子树之内的路径,并不要跨过它和它父亲的这条边,这样就可以避免重复计算答案和遗漏的问题,并且按照树的重心去划分分治点的话,可以使得每一个分治点它目前所分治的子树大小尽可能的小,所以复杂度可以保证到 n log n
还有要注意的就是,就是对于每一个分治点处理完之后由于会重复计算答案,需要对于这个分治点的每一颗子树用容斥的思想减去重复计算的部分。

//#include<bits/stdc++.h>
#include<algorithm>
#include<cstdio>
#include<cstring>

#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;

using namespace std;

void File(){
    freopen("poj1741.in","r",stdin);
    freopen("poj1741.out","w",stdout);
}

template<typename T>bool chkmax(T &_,T __){return _<__ ? (_=__,1) : 0;}
template<typename T>bool chkmin(T &_,T __){return _>__ ? (_=__,1) : 0;}

const int maxn=1e4+10;
const int inf=0x3f3f3f3f;
int n,k;
ll ans;
int dis[maxn],cnt_dis;
int sz[maxn],Minsz,tot_sz,root;
int to[maxn<<1],las[maxn<<1],beg[maxn],val[maxn<<1],cnte=1;
bool vis[maxn];

void add(int u,int v,int w){
    las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v; val[cnte]=w;
    las[++cnte]=beg[v]; beg[v]=cnte; to[cnte]=u; val[cnte]=w;
}

void get_root(int u,int f){
    int Maxsz=0; sz[u]=1;
    for(int i=beg[u];i;i=las[i]){
        if(vis[to[i]] || to[i]==f)continue;
        get_root(to[i],u);
        sz[u]+=sz[to[i]];
        chkmax(Maxsz,sz[to[i]]);
    }
    chkmax(Maxsz,tot_sz-sz[u]);
    if(chkmin(Minsz,Maxsz))root=u;
}

void get_dis(int u,int f,int d){
    dis[++cnt_dis]=d;
    for(int i=beg[u];i;i=las[i]){
        if(to[i]==f || vis[to[i]])continue;
        get_dis(to[i],u,d+val[i]);
    }
}

ll solve(int u,int len){
    ll ret=0;
    cnt_dis=0; get_dis(u,0,0);
    sort(dis+1,dis+cnt_dis+1);
    int l=1,r=cnt_dis;
    while(l<r)
        if(dis[l]+dis[r]+2*len<=k)ret+=r-l,++l;
        else --r;
    return ret;
}

void divide(int u){
    vis[u]=1; get_root(u,0);
    ans+=solve(u,0);
    for(int i=beg[u];i;i=las[i]){
        if(vis[to[i]])continue;
        ans-=solve(to[i],val[i]);
        Minsz=inf; tot_sz=sz[to[i]];
        get_root(to[i],0);
        divide(root);
    }
}

int main(){
    File();
    while(~scanf("%d%d",&n,&k)){
        if(!n && !k)break;
        ans=0; cnte=1;
        memset(beg,0,sizeof(beg));
        memset(vis,0,sizeof(vis));
        int u,v,w;
        REP(i,1,n-1){
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);
        }
        Minsz=inf; tot_sz=n;
        get_root(1,0);
        divide(root);
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ylsoi/article/details/81516251
今日推荐