POJ 1741 Tree(点分治)

http://poj.org/problem?id=1741

题意:

给出一棵树,求出树上满足两点权值之和不大于k的点对数。

 

思路:

直接暴力就是$O(n^2)$,显然不行。这里有一篇论文可以推荐大家看一下

因为一条路径要么过根结点,要么不过,即在一棵子树中,然后我们可以用分治法。每次确定一个根u,u有几棵子树,就将u的子树分成了几部分,每一部分就一直递归下去。

我们设dis[i]为i到根结点的距离,于是我们可以得到以下的算法:$dis[i]+dis[j]<=k的(i,j)对数-dis[i]+dis[j]<=k的(i,j)对数且(i,j)在一棵子树中$。这个部分其实不难算,我们只需要先把所有子节点的dis值求出,然后排个序,利用尺取法可以快速求出个数。

还有一点要注意的是,根结点的选择很重要,这里需要选择重心,因为删去重心后最大子树的节点数最小。

  1 #include<iostream>
  2 #include<algorithm>
  3 #include<cstring>
  4 #include<cstdio>
  5 #include<vector>
  6 #include<stack>
  7 #include<queue>
  8 #include<cmath>
  9 #include<map>
 10 #include<set>
 11 using namespace std;
 12 typedef long long ll;
 13 typedef pair<int,int> pll;
 14 const int INF = 0x3f3f3f3f;
 15 const int maxn=10000+5;
 16 
 17 int n,k,num,root,sz,ans,mi;
 18 int vis[maxn], son[maxn], dis[maxn], mx[maxn];
 19 vector<pll> G[maxn];
 20 
 21 
 22 void getroot(int u, int fa, int n) //计算重心
 23 {
 24     son[u]=0;
 25     int balance=0;
 26     for(int i=0;i<G[u].size();i++)
 27     {
 28         int v=G[u][i].first;
 29         if(v==fa || vis[v])  continue;
 30         getroot(v,u,n);
 31         son[u]+=son[v]+1;
 32         balance=max(balance,son[v]+1);
 33     }
 34     balance=max(balance,n-son[u]-1);
 35     if(balance<sz)
 36     {
 37         sz=balance;
 38         root=u;
 39     }
 40 }
 41 
 42 
 43 void getdis(int u, int fa, int d)  //求子节点到根结点的距离
 44 {
 45     dis[num++]=d;
 46     for(int i=0;i<G[u].size();i++)
 47     {
 48         int v=G[u][i].first;
 49         int w=G[u][i].second;
 50         if(v==fa || vis[v])  continue;
 51         getdis(v,u,d+w);
 52     }
 53 }
 54 
 55 int getnum()
 56 {
 57     int ret=0;
 58     sort(dis,dis+num);
 59     int l=0,r=num-1; 
 60     while(l<r)  //尺取法快速求对数
 61     {
 62         while(dis[l]+dis[r]>k && l<r)  r--;
 63         ret+=r-l;
 64         l++;
 65     }
 66     return ret;
 67 }
 68 
 69 void solve(int u, int n)
 70 {
 71     sz=INF;
 72     getroot(u,-1,n);
 73     num=0;
 74     getdis(root,-1,0);
 75     ans+=getnum();
 76     vis[root]=1;
 77     int tmp=root;
 78     for(int i=0;i<G[tmp].size();i++)
 79     {
 80         int v=G[tmp][i].first;
 81         int w=G[tmp][i].second;
 82         if(!vis[v])
 83         {
 84             num=0;
 85             getdis(v,-1,w);  //减去在同一棵子树的情况
 86             ans-=getnum();
 87             solve(v,son[v]+1);
 88         }
 89     }
 90 }
 91 
 92 int main()
 93 {
 94     //freopen("in.txt","r",stdin);
 95     while(~scanf("%d%d",&n,&k))
 96     {
 97         if(!n && !k)  break;
 98         memset(vis,0,sizeof(vis));
 99         for(int i=1;i<=n;i++)  G[i].clear();
100         for(int i=1;i<n;i++)
101         {
102             int u,v,w;
103             scanf("%d%d%d",&u,&v,&w);
104             G[u].push_back(make_pair(v,w));
105             G[v].push_back(make_pair(u,w));
106         }
107         ans=0;
108         solve(1,n);
109         printf("%d\n",ans);
110     }
111     return 0;
112 }

 

posted @ 2017-08-28 20:10  Kayden_Cheung  阅读(139)  评论(0编辑  收藏  举报
//目录