poj 1741 点分治

来源:互联网 发布:mysql 基数低 不走索引 编辑:程序博客网 时间:2024/05/22 17:51

点分治就是在树上跑分治。

这个题是求树上两两距离小于K的点对有多少.

可以定义length[i]为i点与根结点的距离。

那么对于其不在同一个根儿子子树的点,这样可以全部计算出来了,现在就差每个子树里的点。也就是说,对于这个根,经过其的点的路径全部算出来了。

此时可以把这个根从图中删掉,那么再递归进行处理后面的根,那么得到的结果即可。

每次都寻找重心来进行分治,这样可以使得复杂度为 nlogn

注意写代码的正确性。。


#include <iostream>#include <stdio.h>#include <vector>#include <string.h>#include <algorithm>#include <map>#include<time.h>#include<set>using namespace std;const int maxn=1e4+7;struct ttt{int w,v,next;}edge[maxn+maxn];int head[maxn];int e,n;bool vis[maxn];void init(){    e=0;    memset(vis,0,sizeof(vis));    memset(head,-1,sizeof(head));}int K;void add(int u,int v,int w){    e++;    edge[e].v=v;    edge[e].w=w;    edge[e].next=head[u];    head[u]=e;}int mx[maxn],size[maxn],mi,root,dis[maxn],num;//mx用来储存每个结点的最大子树大小,size储存每个结点的子树大小,dis存结点int dfssize(int u,int fa){ //这个函数预处理全部子树的大小和每个点的最大子树大小    int i,v;   //cout <<" in in " << u << endl;    size[u]=1; //初始大小为1,最大子树大小是0    mx[u]=0;    for(i=head[u];i!=-1;i=edge[i].next){        v=edge[i].v;     //   cout <<"!!!"<<v<< endl;        if(v != fa && !vis[v]){            dfssize(v,u);        size[u]+=size[v];        mx[u]=max(size[v],mx[u]);        }    }  //  cout <<" OUT OUT  " << u << endl;}//dfsroot 的fa只用来判不走回去的边,r用来判从进入到这个点的这个方向的点有多少int dfsroot(int r,int u,int fa){ //r是指最开始dfs的那个点    int i,v;    if(size[r]-size[u]>mx[u])mx[u]=size[r]-size[u]; //这里是考虑从父节点连接下来的那一边    if(mx[u]<mi)mi=mx[u],root=u;//取最大子树最小的那个点作为根    for(i=head[u];i!=-1;i=edge[i].next){        int v=edge[i].v;        if(vis[v]==1||v==fa)continue;        dfsroot(r,v,u);    }}int dfsdis(int u,int d,int fa){    dis[num++]=d; //距离为d    int i,v;    for(i=head[u];i!=-1;i=edge[i].next){        v=edge[i].v;        if(vis[v]==1||v==fa)continue;        dfsdis(v,d+edge[i].w,u);    }}int calc(int u,int d){//到这个点的距离已经是d了    int ret=0;    num=0;    dfsdis(u,d,0); //从这个点开始计算出全部点的距离    sort(dis,dis+num);    int i=0,j=num-1;    while(i<j){ //双指针扫        while(dis[i]+dis[j]>K&&i<j)j--;        ret+=j-i;i++;    }    return ret;}int Sum;int dfs(int u){  //  cout <<"in"<< u<< endl;    mi=n;    dfssize(u,0);    dfsroot(u,u,0);    Sum+=calc(root,0); //以root作为根去找    vis[root]=1;    int i,v;    for(i=head[root];i!=-1;i=edge[i].next){        v=edge[i].v;        if(!vis[v]){ //每次计算的时候会把同一颗子树上点对也计算了,这样子会重复计算,那么每次剪掉即可            Sum-=calc(v,edge[i].w);            dfs(v);        }    }}int main(){    int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4,m;    //freopen("in.txt","r",stdin);    while(scanf("%d %d",&n,&m)==2){    if(n+m==0)break;    K=m;init();    for(i=1;i<n;i++){        scanf("%d %d %d",&t1,&t2,&t3);        add(t1,t2,t3);        add(t2,t1,t3);    }    //for(i=head[1];i!=-1;i=edge[i].next){      //  cout << edge[i].v <<endl;    //}    Sum=0;    dfs(1);    printf("%d\n",Sum);    }    return 0;}


原创粉丝点击