点分治模板(洛谷P4178 Tree)(树分治,树的重心,容斥原理)

推荐YCB的总结

推荐你谷ysn等巨佬的详细题解

大致流程——

  1. dfs求出当前树的重心

  2. 对当前树内经过重心的路径统计答案(一条路径由两条由重心到其它点的子路径合并而成)

  3. 容斥减去不合法情况(两条子路径在重心的子树内就已经相交)

  4. 删除重心(打上永久标记),对子树继续处理,转1

求重心是板子,算答案的方法要依题而定,一般都要容斥。

模板题洛谷传送门

calc函数中,头尾两个指针扫的计数方法也是一种套路

因为要sort,所以复杂度\(O(n\log^2n)\),不过蒟蒻实测你谷数据\(k\)不超过\(40000\),所以可以改用桶排序,复杂度降到\(O(n(\log n+\log k))\)

#include<cstdio>
#include<cstring>
#include<algorithm>
#define RG register
#define I inline
#define R RG int
#define G c=getchar()
using namespace std;
const int N=4e5+9,M=8e5+9;
int n,k,p,rt,ans,he[N],ne[M],to[M],l[M],mx[N],s[N],b[N];
bool vis[N];
I void max(R&x,R y){if(x<y)x=y;}
I int in(){
    RG char G;
    while(c<'-')G;
    R x=c&15;G;
    while(c>'-')x*=10,x+=c&15,G;
    return x;
}
void getrt(R x){//求重心模板
    vis[x]=1;s[x]=1;mx[x]=0;
    for(R y,i=he[x];i;i=ne[i]){
        if(vis[y=to[i]])continue;
        getrt(y);
        s[x]+=s[y];max(mx[x],s[y]);
    }
    max(mx[x],n-s[x]);
    if(mx[rt]>mx[x])rt=x;
    vis[x]=0;
}
void getd(R x,R d){//统计长度不超过k的子路径
    if(d>k)return;
    vis[x]=1;b[++p]=d;
    for(R i=he[x];i;i=ne[i])
        if(!vis[to[i]])getd(to[i],d+l[i]);
    vis[x]=0;
}
I int calc(R x,R dis){//计算经过x的路径的答案
    p=0;getd(x,dis);
    sort(b+1,b+p+1);
    R ret=0,i=1,j=p;//双指针扫描计数
    while(i<=j)b[i]+b[j]>k?--j:ret+=j-i++;
    return ret;
}
void div(R x){//分治流程在此函数中得到体现
    getrt(x);
    ans+=calc(x=rt,0);
    vis[x]=1;rt=0;
    for(R t=n,y,i=he[x];i;i=ne[i]){
        if(vis[y=to[i]])continue;
        ans-=calc(y,l[i]);
        n=s[x]>s[y]?s[y]:t-s[x];
        div(y);
    }
}
int main(){
    mx[0]=1e9;
    n=in();
    for(R x,y,i=1;i<n;++i){
        x=in();y=in();
        ne[++p]=he[x];to[he[x]=p]=y;
        ne[++p]=he[y];to[he[y]=p]=x;
        l[p]=l[p-1]=in();
    }
    k=in();
    div(1);
    printf("%d\n",ans);
    return 0;
}
posted @ 2018-07-22 21:29  Flash_Hu  阅读(232)  评论(0编辑  收藏  举报