poj1741 tree 点分治

把这几天写的东西上传一下
写博客的意义大概就是整理一下思路 以及方便日后复习
不得不说 把代码放在文件夹里
做完我是不会过目的
写博客 或许会增加我的成就感
从而 会多注意一下吧
哈哈 csdn现在也高大上了很多嘛
poj1741 Tree
题目大意:求(u,v)满足dis(u,v)<=k的点对数 N<=10000
点分治 LNWC2016新学到的 Orz
大概是 把所有的边分为经过点 x (重心) 和不经过点x
不经过点x的 多棵子树递归处理
可以证明递归次数不超过 logN 次
刚学完时候想尝试徒手写失败了= =
参考了hzwer的点分治= =
在这里%%%
但是我发现了个问题 点分治每次找的不是重心 但黄学长说没影响 不知道为什么
此题题解:
对于每次处理的根节点
求出它的子树所有点到距离 排序
ans+= dis(u,root)+dis(root,v)<=k 的点对数
但对于(u,v)在一棵子树上的情况 本不应该算入
所以答案还要减去(u,v)在一棵子树的情况

这个点分治是模仿hzwer实现的 有时间再写一发这题或别的题QAQ
在这里立个flag呦

#include<iostream>
#include<cstdio> 
#include<algorithm>
using namespace std;
const int N=10010;
const int inf=0x7fffffff;
struct E {int to,nxt,val;}edge[N*2];
int idx[N],can[N],size[N],deep[N],d[N],f[N];
int tot,root,ans,ret,sum,p;
int n,k;
void init(){
    tot=1;root=0;ans=0;sum=n;
    fill(idx,idx+N,0);
    fill(can,can+N,1);
    fill(size,size+N,0);
    fill(deep,deep+N,0);
    fill(d,d+N,0);
    fill(f,f+N,0);f[0]=inf;
}
void addedge(int from,int to,int val){
    edge[tot].to=to;edge[tot].val=val;edge[tot].nxt=idx[from];idx[from]=tot++;
}
void getroot(int x,int fa){
    size[x]=1;f[x]=0;
    for(int t=idx[x];t;t=edge[t].nxt){
        E e=edge[t];
        if(can[e.to] && e.to!=fa){
            getroot(e.to,x);
            f[x]=max(f[x],size[e.to]);
            size[x]+=size[e.to];
        }
    }
    f[x]=max(f[x],sum-size[x]);
    if(f[x]<f[root]) root=x;
}
void getdeep(int x,int fa){
    deep[++p]=d[x];
    for(int t=idx[x];t;t=edge[t].nxt){
        E e=edge[t];
        if(can[e.to] && e.to!=fa) {
            d[e.to]=d[x]+e.val;
            getdeep(e.to,x);
        }
    }
}
int calc(int x,int now){
    ret=0;d[x]=now;p=0;
    getdeep(x,0);
    sort(deep+1,deep+1+p);
    int r=p;
    for(int l=1;l<=p;l++){
        while(deep[l]+deep[r]>k) r--;
        if(r<=l) break;
        ret+=(r-l);
    }
    return ret;
}
void work(int x){
    ans+=calc(x,0);
    can[x]=0;
    for(int t=idx[x];t;t=edge[t].nxt){
        E e=edge[t];
        if(can[e.to]){
            sum=size[e.to];
            ans-=calc(e.to,e.val);
            root=0;
            getroot(e.to,0);
            work(root);
        }
    }
}
int main(){
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    while(1){
        scanf("%d%d",&n,&k);
        if(n==0 && k==0) break;
        init();
        for(int i=1;i<n;i++){
            int x,y,w;
            scanf("%d%d%d",&x,&y,&w);
            addedge(x,y,w);addedge(y,x,w);
        }
        getroot(1,0);
        work(root);
        printf("%d\n",ans);
    }
    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值