【POJ】【1741】/【BZOJ】【1468】Tree
点分治
怎么又一道叫Tree的题目……真是醉了。
本题为漆子超论文《分治算法在树的路径问题中的应用》例一
题解 : http://blog.csdn.net/sdj222555/article/details/7893862
http://blog.csdn.net/yang_7_46/article/details/9966455
既然是点分治嘛,为了保证复杂度不退化,必然要找树的重心,这一步可以通过一次dfs实现:以任意节点为根(方便起见就选1号节点了)求出每个节点子树大小,那么当前节点就将整棵树分为size[x],n-size[x]两部分,我们取较大的那一部分作为当前节点的子树大小(只是记录个值而已, 不必在意具体哪个是根),然后我们可以在dfs到每个节点时与全局变量root比较一下 f[x]和f[root] 如果f[x]<f[root] 则root=x(这里用root表示重心)(由于f[x]>=n/2,所以f[x]最小的那个x就是将整棵树分的最平均的那个“重心”)
找到重心后,需要统计所有点(n-1个)到重心的距离,统计有多少对点之间的距离小于等于k。同样用一次dfs实现,然后将所有节点到重心的距离存在一个数组里,进行一次快排~然后利用单调性,一次相向搜索在O(n)时间内解决。但是这是有额外计数的:两点在同一子树里的是不能算的,所以减去两点在同一子树里的情况(递归求解子树的时候会再算一次)。当然还是要递归算各子树内的点对啦~
1 //POJ 1741 2 #include<cstdio> 3 #include<vector> 4 #include<cstring> 5 #include<cstdlib> 6 #include<iostream> 7 #include<algorithm> 8 #define rep(i,n) for(int i=0;i<n;++i) 9 #define F(i,j,n) for(int i=j;i<=n;++i) 10 #define D(i,j,n) for(int i=j;i>=n;--i) 11 #define pb push_back 12 using namespace std; 13 void read(int &v){ 14 v=0; int sign=1; char ch=getchar(); 15 while(ch<'0'||ch>'9'){ if (ch=='-') sign=-1; ch=getchar();} 16 while(ch>='0'&&ch<='9'){ v=v*10+ch-'0'; ch=getchar();} 17 v*=sign; 18 } 19 /******************tamplate*********************/ 20 const int N=10086; 21 struct edge{ 22 int to,l; 23 }; 24 int n,k,size,s[N],f[N],root,d[N],ans,K; 25 vector<edge>G[N]; 26 vector<int>dep; 27 bool vis[N]; 28 29 void getroot(int x,int fa){ 30 int y; 31 s[x]=1;f[x]=0; 32 rep(i,G[x].size()){ 33 y=G[x][i].to; 34 if (y!=fa && !vis[y]){ 35 getroot(y,x); 36 s[x]+=s[y]; 37 f[x]=max(f[x],s[y]); 38 } 39 } 40 f[x]=max(f[x],size-s[x]); 41 if (f[x]<f[root]) root=x; 42 } 43 void getdep(int x,int fa){ 44 int y; 45 dep.pb(d[x]); 46 s[x]=1; 47 rep(i,G[x].size()){ 48 y=G[x][i].to; 49 if (y!=fa && !vis[y]){ 50 d[y]=d[x]+G[x][i].l; 51 getdep(y,x); 52 s[x]+=s[y]; 53 } 54 } 55 } 56 int calc(int x,int init){ 57 dep.clear(); d[x]=init; 58 getdep(x,0); 59 sort(dep.begin(),dep.end()); 60 int ans=0; 61 for(int l=0,r=dep.size()-1; l<r; ) 62 if (dep[l]+dep[r]<=k) ans+=r-l++; 63 else r--; 64 return ans; 65 } 66 void work(int x){ 67 int y; 68 ans+=calc(x,0); 69 vis[x]=1; 70 rep(i,G[x].size()){ 71 y=G[x][i].to; 72 if (!vis[y]){ 73 ans-=calc(y,G[x][i].l); 74 f[0]=size=s[y]; 75 getroot(y,root=0); 76 work(root); 77 } 78 } 79 } 80 int main(){ 81 // freopen("1741.in","r",stdin); 82 while(scanf("%d%d",&n,&k)==2){ 83 if (n==0 && K==0) break; 84 int x,y,z; 85 F(i,1,n) G[i].clear(); 86 memset(vis,0,sizeof vis); 87 F(i,2,n){ 88 read(x); read(y); read(z); 89 G[x].pb((edge){y,z}); G[y].pb((edge){x,z}); 90 } 91 f[0]=size=n; 92 getroot(1,root=0); 93 ans=0; 94 work(root); 95 printf("%d\n",ans); 96 } 97 return 0; 98 }
1468: Tree
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 606 Solved: 317
[Submit][Status][Discuss]
Description
给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K
Input
N(n<=40000)
接下来n-1行边描述管道,按照题目中写的输入
接下来是k
Output
一行,有多少对点之间的距离小于等于k
Sample Input
7
1 6 13
6 3 9
3 5 7
4 1 3
2 4 20
4 7 2
10
1 6 13
6 3 9
3 5 7
4 1 3
2 4 20
4 7 2
10
Sample Output
5