POJ - 1741(树分治,n较大的存权方法)

树分治策略,

本题目直接爆o(n^2)肯定会超时,采取分治策略,每次dfs子树都要先找到重心,这样深度不超过logn层

然后鬼每一个重心只统计过该点的不超过k的路径数,要统计该值首先要先得到所有子节点到该点的距离,然后排序,排序之后可用o(n)的相向搜索得到;

需注意必须再减去在同一子树中两点构成的小于等于k的路径;因为这条路径不合法;总复杂度nlognlogn

这里给出复杂度证明:

       第一层为nlogn

       假设第二层为有三颗子树其数量记为n1,n2,n3( n1+n2+n3=n-1 ) 则在该层耗时 n1logn1  + n2logn2 + n3logn3 <n1logn++ n2logn+ n3logn < nlogn

      所以总层数为logn层  所以总共耗时 t < n logn*logn

#include <cstdio>
#include <map>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

struct node{
int x,y;
node(int x=0,int y=0):x(x),y(y){}
bool operator <(const node& rhs)const{
return x<rhs.x || x==rhs.x&&y<rhs.y;
}
};
const int maxn = 10110;
vector<node> G[maxn];
int focus,n,MIN,pre[maxn],K,nown;
int get_focus(int u,int f){
     int tot = 1,min_=0;
     for(int i=0;i<G[u].size();i++){
         if(pre[G[u][i].x]) continue;
         if(G[u][i].x==f) continue;
         int temp = get_focus(G[u][i].x,u);
         tot+=temp;
         min_=max(min_,temp);
     }
     min_=max(min_,nown-tot);
     if(min_ < MIN){
          MIN = min_; focus=u;
     }
     return tot;
}
int Get_focus(int u,int f,int te){
   nown = te;
   MIN = n; get_focus(u,f);
   return  focus;
}
vector<int> dis;
int res,CNT;
void get_dis(int u,int f,int add){
    dis.push_back(add); CNT++;
    for(int i=0;i<G[u].size();i++){
       if(pre[G[u][i].x]) continue;
       if(G[u][i].x==f)   continue;
       get_dis(G[u][i].x,u,add+G[u][i].y);
    }
}
int solve(int u,int f,int add){
    dis.clear();
    get_dis(u,f,add);
    sort(dis.begin(),dis.end());
    int num = 0;
    for(int i=0,j=dis.size()-1;i<j;i++){
        while(j>i&&dis[j] + dis[i]>K) j--;
        if(i<j) num+=j-i;
    }
    return num;
}
void dfs(int u,int f){
   res+=solve(u,f,0);
   pre[u] = 1;
   for(int i=0;i<G[u].size();i++){
       if(pre[G[u][i].x]) continue;
       if(G[u][i].x==f)  continue;
       CNT = 0;
       res-=solve(G[u][i].x,u,G[u][i].y);
       Get_focus(G[u][i].x,u,CNT);
       dfs(focus,-1);
   }
}
int main()
{
     while(scanf("%d %d",&n,&K)==2){
          if(!n&&!K) break;
          for(int i=1;i<=n;i++) G[i].clear();
          memset(pre,0,sizeof(pre));
          for(int i=1;i<n;i++){
              int x,y,z;
              scanf("%d %d %d",&x,&y,&z);
              G[x].push_back(node(y,z));
              G[y].push_back(node(x,z));
          }
          Get_focus(1,-1,n);
          res=0;
          dfs(focus,-1);
          printf("%d\n",res);
     }
     return 0;
}
/*
5 1
1 2 1
2 3 1
3 4 1
4 5 1
*/



你可能感兴趣的:(C++,算法,namespace,poj)