点分治

 点分治是个好东西 学长说今年省选day2 T3 有40分可以是点分治 有点难受如果我当初学了点分治该有多好啊。

所谓点分治就是在树上搞一些分治操作让复杂度大大降低。

这道题询问树上是否有任意两点之间的距离为k 考虑暴力 m指询问数 

暴力枚举 加dfs跑距离 所以复杂度是mn^3 期望得分 30

由于是一棵无根树所以考虑以1位根节点提前预处理出树上任意点距根的距离。

然后 暴力枚举两个端点 求出LCA 然后 d[x]+d[y]-(d[LCA]<<1)判断即可。

复杂度 mn^2logn期望得分60 很不错了~

正解释点分治:其实点分治是一种另类的分治方法基于树上的分治。

先处理过根的边 然后分治处理根的子树这样递归处理 考虑每次选根都是树的重心处。

所以总递归层数为logn 在每一层中暴力判断和过根的边的联系的边然后进行判断复杂度O(n)

总复杂度mnlogn 期望得分:100;这样巧妙的利用了分治思想和不断的选择重心 使复杂度骤降。

因为一些小细节打挂了所以浪费了点时间来检查。

首先是对于树的重心的处理 然后是分治点 然后在每个点中进行答案的统计即可。

复杂度mnlogn 可以AC。

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<vector>
#include<ctime>
#define INF 2147483646
#define ll long long
#define max(x,y) (x>y?x:y)
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
inline void put(int x)
{
    x<0?putchar('-'),x=-x:0;
    int num=0;char ch[50];
    while(x)ch[++num]=x%10+'0',x/=10;
    num==0?putchar('0'):0;
    while(num)putchar(ch[num--]);
    putchar('\n');return;
}
//点分治 Author:chdy
const int MAXN=10002,maxn=10000002;
int n,m,k,len,sum,t,h;//sum 当前递归到的这颗子树大小
int lin[MAXN<<1],nex[MAXN<<1],ver[MAXN<<1],e[MAXN<<1];
int vis[MAXN],f[MAXN];//f[i]表示以i为根时的最大子树大小
int sz[MAXN],query[MAXN];//表示以i为根时其子树的节点个数
int ans[MAXN],q[MAXN],dis[MAXN],tmp[MAXN];
bool judge[maxn];
int root;
inline void add(int x,int y,int z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
inline void getroot(int x,int fa)
{
    f[x]=0;sz[x]=1;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==fa||vis[tn])continue;//注意两个条件
        getroot(tn,x);
        sz[x]+=sz[tn];
        f[x]=max(f[x],sz[tn]);
    }
    f[x]=max(f[x],sum-sz[x]);
    if(f[x]<f[root]||root==0)root=x;
    return;
}
inline void getdis(int x,int father)
{
    tmp[++h]=dis[x];
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father||vis[tn])continue;//注意两个条件
        dis[tn]=dis[x]+e[i];
        getdis(tn,x);
    }
}
void carcluate(int x)
{
    t=0;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(vis[tn])continue;
        dis[tn]=e[i];h=0;
        getdis(tn,x);
        for(int j=1;j<=m;++j)
            for(int k=1;k<=h;++k)
            if(query[j]>=tmp[k])ans[j]|=judge[query[j]-tmp[k]];
        for(int j=1;j<=h;++j)q[++t]=tmp[j],judge[tmp[j]]=1;
    }
    for(int i=1;i<=t;++i)judge[q[i]]=0;
    return;
}
inline void sol(int x)
{
    vis[x]=1;judge[0]=1;
    carcluate(x);
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(vis[tn])continue;
        sum=sz[tn];root=0;
        getroot(tn,x);
        if(sz[tn]==1)continue;
        sol(root);
    }
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();
    for(int i=1;i<n;++i)
    {
        int x,y,z;
        x=read();y=read();z=read();
        add(x,y,z);add(y,x,z);
    }
    for(int i=1;i<=m;++i)query[i]=read();
    sum=n;root=0;
    getroot(1,0);sol(root);
    for(int i=1;i<=m;++i)ans[i]?puts("AYE"):puts("NAY");
    return 0;
}
View Code

这道题是一个裸的点分治 我也就码了1h 然而错误百出可能是心不在焉吧。

什么数组开大 什么边都存错 什么重心找错 什么变量名打错 什么求出重心后没有使用。我弃疗了。

但是 坚持检查了20min 在爆零两次的基础之下 艰难的AC了(我是真的菜)

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<vector>
#include<ctime>
#define INF 2147483646
#define ll long long
#define mod 3
#define max(x,y) (x>y?x:y)
#define min(x,y) (x>y?y:x)
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
inline void put(int x)
{
    x<0?putchar('-'),x=-x:0;
    int num=0;char ch[50];
    while(x)ch[++num]=x%10+'0',x/=10;
    num==0?putchar('0'):0;
    while(num)putchar(ch[num--]);
    putchar('\n');return;
}
//点分治 Author:chdy
const int MAXN=20002;
int n,sum,s1,s2,t,h,root,g,len;
int lin[MAXN<<1],nex[MAXN<<1],ver[MAXN<<1],e[MAXN<<1];
int sz[MAXN],f[MAXN],judge[4],q[MAXN],dis[MAXN],vis[MAXN];
int gcd(int a,int b){return b?gcd(b,a%b):a;}
inline void add(int x,int y,int z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
void getroot(int x,int father)
{
    sz[x]=1;f[x]=0;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father||vis[tn])continue;
        getroot(tn,x);
        sz[x]+=sz[tn];
        f[x]=max(f[x],sz[tn]);
    }
    f[x]=max(f[x],sum-sz[x]);
    if(f[x]<f[root]||root==0)root=x;
    return;
}
void getdis(int x,int father)
{
    q[++t]=dis[x];
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father||vis[tn])continue;
        dis[tn]=dis[x]+e[i];
        getdis(tn,x);
    }
    return;
}
void carcluate(int x)
{
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(vis[tn])continue;
        t=0;dis[tn]=e[i];
        getdis(tn,x);
        for(int j=1;j<=t;++j)s1+=judge[(mod-q[j]%mod)%mod];
        for(int j=1;j<=t;++j)++judge[q[j]%mod];
    }
    judge[1]=0;judge[2]=0;
    return;
}
void sol(int x)
{
    vis[x]=1;judge[0]=1;
    carcluate(x);
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(vis[tn])continue;
        if(sz[tn]==1)continue;
        root=0;sum=sz[tn];
        getroot(tn,x);
        sol(root);
    }
    return;
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<n;++i)
    {
        int x,y,z;
        x=read();y=read();z=read();
        add(x,y,z);add(y,x,z);
    }
    sum=n;s2=n*n;getroot(1,0);
    sol(root);s1=s1*2+n;
    g=gcd(s2,s1);
    printf("%d/%d\n",s1/g,s2/g);
    return 0;
}
View Code

其实这道题应该是树形dp 在我A掉后看题解发现树形dp非常简单而且貌似复杂度还是线性的。

上面好像没有提到点分治的复杂度 的计算首先 为了保证不是n^2的点分治

我们必须求重心 求出来重心之后 最多被分出n/2大小的子树再对这颗子树进行分治最大子树为n/4

这样一棵树被我们递归了logn层 然后求重心是O(n)的 再加上分治时的计算也是O(n)的

总复杂度 nlogn.非常巧妙 的算法 。

下面是dp写法:设f[x][j]表示以x为根节点为j的数量。

然后考虑状态转移和答案的统计 具体细节还是很好想的。

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<vector>
#include<ctime>
#define INF 2147483646
#define ll long long
#define mod 3
#define max(x,y) (x>y?x:y)
#define min(x,y) (x>y?y:x)
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
inline void put(int x)
{
    x<0?putchar('-'),x=-x:0;
    int num=0;char ch[50];
    while(x)ch[++num]=x%10+'0',x/=10;
    num==0?putchar('0'):0;
    while(num)putchar(ch[num--]);
    putchar('\n');return;
}
//树形dp Author:chdy
const int MAXN=20002;
int n,s2,g,len,ans;
int vis[MAXN];
int f[MAXN][3];//f[i][j]表示在i这颗子树当中j的数量
//显然的状态转移是 f[tn][j]->f[x][(j+e[i])%mod]
int lin[MAXN<<1],nex[MAXN<<1],ver[MAXN<<1],e[MAXN<<1];
int gcd(int a,int b){return b?gcd(b,a%b):a;}
inline void add(int x,int y,int z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
void dp(int x)
{
    vis[x]=1;f[x][0]=1;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(vis[tn])continue;
        dp(tn);
        for(int j=0;j<=2;++j)ans+=f[tn][j]*(f[x][(mod-(j+e[i])%mod)%mod]);
        for(int j=0;j<=2;++j)f[x][(j+e[i])%mod]+=f[tn][j];
    }
    return;
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<n;++i)
    {
        int x,y,z;
        x=read();y=read();z=read();
        add(x,y,z);add(y,x,z);
    }
    dp(1);s2=n*n;
    ans=(ans<<1)+n;g=gcd(s2,ans);
    printf("%d/%d\n",ans/g,s2/g);
    return 0;
}
View Code

还有一道例题:

求 一条长度为k的路径且边数最小 一眼 树形dp吧 然后发现 

f[n][k]早就爆掉了空间我们不能这样dp 那么暴力一点点分治 吧 复杂度先算好 没有extra操作那么复杂度为nlogn

感觉和第一题差不多只不过是求最少边数 想象一下 我能否维护这个性质

应该是可以的吧 在统计时判断一下即可 至于如何统计路径数貌似只需在第一题的基础上加一些小判断吧。

然后被打脸了 没有缜密的思维 如何快速A题呢 我觉得我还没有养成能把所有细节都注意的到的人。

调了将近4h 才调过细节让我几近崩溃。。。

原因:我根本没有考虑到当边权超过k后我如何处理 。第一次我是这样处理的:不作为

导致RE+wa 非常不爽45分 。 RE?我把数组开大 开大十倍 80分接近正确。

然后想到如果有边权是大于k的呢是不是彻底没用啊。

第二次我是这样处理的:if(z>maxn-20)continue; 

这样的做法绝对是错误的,因为失去一条边意味着整张图都不再连通了 我在连通块中又没做点分治。

然后还是wa 看看题解 发现跟题解上处理方法不一样 然后改成题解的方法(我改的是个假的)

第三次我是这样处理的:

void getdis(int x,int father)
{
    q[++t]=dis[x];
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father||vis[tn])continue;
        if(dis[x]+e[i]>k)return;
        dis[tn]=dis[x]+e[i];
        getdis(tn,x);
    }
    return;
}
View Code

这个return 看似和题解上的差不多 其实差远了。

其中再次记录我wa掉一个点的原因 :问号表达式写挂了 95分 问号表达式改成if语句就过了 可能是我的问号表达式写的太玄学了。

不能这样因为可能旁边的边还是合法的我这个一不合法就直接return 必定得到错误答案。

应该是dfs 型来getdis才对这样可以使很多信息得到。

inline void getdis(int x,int father,int d1,int d2)
{
    if(d1>k)return;
    tmp[++h]=x;dis[x]=d1;w[x]=d2;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father||vis[tn])continue;
        getdis(tn,x,d1+e[i],d2+1);
    }
}
View Code

当然这样做也是可以的:

inline void getdis(int x,int father)
{
    tmp[++h]=x;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father||vis[tn])continue;
        if(dis[x]+e[i]>k)continue;
        dis[tn]=dis[x]+e[i];
        w[tn]=w[x]+1;
        getdis(tn,x);
    }
}
View Code

思维一定要缜密 代码一定要简洁。

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<vector>
#include<ctime>
#define INF 2147483646
#define ll long long
#define judge(i) s[i].judge
#define b(i) s[i].b
#define max(x,y) (x>y?x:y)
#define min(x,y) (x>y?y:x)
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
inline void put(int x)
{
    x<0?putchar('-'),x=-x:0;
    int num=0;char ch[50];
    while(x)ch[++num]=x%10+'0',x/=10;
    num==0?putchar('0'):0;
    while(num)putchar(ch[num--]);
    putchar('\n');return;
}
//点分治 Author:chdy
const int MAXN=200020,maxn=1000020;
int n,k,maxx=MAXN,len,sum,t,h,root;//sum 当前递归到的这颗子树大小
int lin[MAXN<<1],nex[MAXN<<1],ver[MAXN<<1],e[MAXN<<1];
int vis[MAXN],f[MAXN];//f[i]表示以i为根时的最大子树大小
int sz[MAXN];//表示以i为根时其子树的节点个数
int q[MAXN],dis[MAXN],tmp[MAXN],w[MAXN];
struct wy{int b;bool judge;}s[maxn];
inline void add(int x,int y,int z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
void getroot(int x,int father)
{
    sz[x]=1;f[x]=0;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father||vis[tn])continue;
        getroot(tn,x);
        sz[x]+=sz[tn];
        f[x]=max(f[x],sz[tn]);
    }
    f[x]=max(f[x],sum-sz[x]);
    if(f[x]<f[root]||root==0)root=x;
    return;
}
inline void getdis(int x,int father,int d1,int d2)
{
    if(d1>k)return;
    tmp[++h]=x;dis[x]=d1;w[x]=d2;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father|vis[tn])continue;
        getdis(tn,x,d1+e[i],d2+1);
    }
}
void carcluate(int x)
{
    t=0;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(vis[tn])continue;
        getdis(tn,x,e[i],1);
        for(int j=1;j<=h;++j)
            if(k-dis[tmp[j]]>=0)
                if(judge(k-dis[tmp[j]]))
                    maxx=min(maxx,b(k-dis[tmp[j]])+w[tmp[j]]);
        for(int j=1;j<=h;++j)
        {
            q[++t]=tmp[j];
            if(judge(dis[tmp[j]]))b(dis[tmp[j]])=min(b(dis[tmp[j]]),w[tmp[j]]);
            else judge(dis[tmp[j]])=1,b(dis[tmp[j]])=w[tmp[j]];
        }
    }
    for(int i=1;i<=t;++i)judge(dis[q[i]])=0;
    return;
}
inline void sol(int x)
{
    vis[x]=1;b(0)=0;judge(0)=1;
    carcluate(x);
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(vis[tn])continue;
        if(sz[tn]==1)continue;
        sum=sz[tn];root=0;
        getroot(tn,x);
        sol(root);
    }
}
int main()
{
    freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    n=read();k=read();
    for(int i=1;i<n;++i)
    {
        int x,y,z;
        x=read()+1;y=read()+1;z=read();
        add(x,y,z);add(y,x,z);
    }
    if(k==0){put(0);return 0;}
    sum=n;getroot(1,0);
    sol(root);
    maxx==MAXN?put(-1):put(maxx);
    return 0;
}
View Code

update:发现书上的例题没写就溜了下午写了2h 一A了还不错 。

简单的树上维护一些东西 getdis时在树状数组中统计答案即可 复杂度 nlog^2n k不是很大跑的很快

//#include<bits/stdc++.h>
#include<iomanip>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<stack>
#include<algorithm>
#include<vector>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<map>
#define INF 2147483646
#define ll long long
#define min(x,y) (x>y?y:x)
#define max(x,y) (x>y?x:y)
#define R register
#define up(p,i,n) for(int i=p;i<=n;++i)
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
inline void put(int x)
{
    x<0?x=-x,putchar('-'):0;
    int num=0;char ch[50];
    while(x)ch[++num]=x%10+'0',x/=10;
    num==0?putchar('0'):0;
    while(num)putchar(ch[num--]);
    putchar('\n');return;
}
//点分治 Author:Chdy
const int MAXN=40002,maxn=20002;
int n,len,k,sum,root,t,ans,h;
int q[MAXN],sz[MAXN],f[MAXN],vis[MAXN],c[MAXN],tmp[MAXN];
int lin[MAXN<<1],nex[MAXN<<1],ver[MAXN<<1],e[MAXN<<1];
inline void add1(int x,int y)
{
    for(;x<=maxn;x+=x&(-x))c[x]+=y;
    return;
}
inline int ask(int x)
{
    int cnt=0;
    for(;x;x-=x&(-x))cnt+=c[x];
    return cnt;
}
inline void add(int x,int y,int z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
inline void getroot(int x,int father)
{
    f[x]=0;sz[x]=1;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(vis[tn]||tn==father)continue;
        getroot(tn,x);
        sz[x]+=sz[tn];
        f[x]=max(f[x],sz[tn]);
    }
    f[x]=max(f[x],sum-sz[x]);
    if(f[x]<f[root]||root==0)root=x;
    return;
}
inline void getdis(int x,int father,int dis)
{
    if(dis>k)return;
    tmp[++h]=dis;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father||vis[tn])continue;
        getdis(tn,x,dis+e[i]);
    }
    return;
}
inline void carcluate(int x)
{
    t=0;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(vis[tn])continue;
        h=0;getdis(tn,x,e[i]);
        for(int j=1;j<=h;++j)if(k-tmp[j]!=0)ans+=ask(k-tmp[j]);
        for(int j=1;j<=h;++j)q[++t]=tmp[j],add1(tmp[j],1);
    }
    for(int i=1;i<=t;++i)
    {
        if(q[i]<=k)++ans;
        add1(q[i],-1);
    }
    return;
}
inline void sol(int x)
{
    vis[x]=1;carcluate(x);
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(vis[tn])continue;
        if(sz[tn]==1)continue;
        sum=sz[tn];
        root=0;getroot(tn,x);
        sol(root);
    }
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();
    up(1,i,n-1)
    {
        int x,y,z;
        x=read();y=read();z=read();
        add(x,y,z);add(y,x,z);
    }
    k=read();
    sum=n;getroot(1,0);
    //put(root);
    sol(root);
    put(ans);
    return 0;
}
View Code

题解中 是双指针 (我原来写的双指针觉得不太对环树状数组了)

直接暴力排序 然后双指针进行扫描 然后 一定有不合法的情况容斥一下即可。

//#include<bits/stdc++.h>
#include<iomanip>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<stack>
#include<algorithm>
#include<vector>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<map>
#define INF 2147483646
#define ll long long
#define min(x,y) (x>y?y:x)
#define max(x,y) (x>y?x:y)
#define R register
#define up(p,i,n) for(int i=p;i<=n;++i)
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
inline void put(int x)
{
    x<0?x=-x,putchar('-'):0;
    int num=0;char ch[50];
    while(x)ch[++num]=x%10+'0',x/=10;
    num==0?putchar('0'):0;
    while(num)putchar(ch[num--]);
    putchar('\n');return;
}
//点分治 Author:Chdy
const int MAXN=40002,maxn=20002;
int n,len,k,sum,root,t,ans,h;
int q[MAXN],sz[MAXN],f[MAXN],vis[MAXN],c[MAXN],tmp[MAXN];
int lin[MAXN<<1],nex[MAXN<<1],ver[MAXN<<1],e[MAXN<<1];
inline void add1(int x,int y)
{
    for(;x<=maxn;x+=x&(-x))c[x]+=y;
    return;
}
inline int ask(int x)
{
    int cnt=0;
    for(;x;x-=x&(-x))cnt+=c[x];
    return cnt;
}
inline void add(int x,int y,int z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
inline void getroot(int x,int father)
{
    f[x]=0;sz[x]=1;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(vis[tn]||tn==father)continue;
        getroot(tn,x);
        sz[x]+=sz[tn];
        f[x]=max(f[x],sz[tn]);
    }
    f[x]=max(f[x],sum-sz[x]);
    if(f[x]<f[root]||root==0)root=x;
    return;
}
inline void getdis(int x,int father,int dis)
{
    if(dis>k)return;
    q[++t]=dis;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father||vis[tn])continue;
        getdis(tn,x,dis+e[i]);
    }
    return;
}
inline int carcluate(int x,int d)
{
    int cnt=0;t=0;q[++t]=d;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(vis[tn])continue;
        getdis(tn,x,e[i]+d);
    }
    sort(q+1,q+1+t);
    int l=1,r=t;
    while(l<r)
    {
        if(q[l]+q[r]<=k)cnt+=r-l,++l;
        else --r;
    }
    return cnt;
}
inline void sol(int x)
{
    vis[x]=1;ans+=carcluate(x,0);
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(vis[tn])continue;
        if(sz[tn]==1)continue;
        ans-=carcluate(tn,e[i]);
        sum=sz[tn];
        root=0;getroot(tn,x);
        sol(root);
    }
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();
    up(1,i,n-1)
    {
        int x,y,z;
        x=read();y=read();z=read();
        add(x,y,z);add(y,x,z);
    }
    k=read();
    sum=n;getroot(1,0);
    //put(root);
    sol(root);
    put(ans);
    return 0;
}
View Code
posted @ 2019-04-11 17:14  chdy  阅读(280)  评论(0编辑  收藏  举报