首页 > 代码库 > poj1741+poj1987+poj2114——点分治入门题集合
poj1741+poj1987+poj2114——点分治入门题集合
最近看了看点分治,从poj上找到几道题,都比较裸。而且感觉这三道题都长得差不多呀qwq
————————————————————————————————————————————————
【poj 1741】Tree
题意:给定一棵边带权的树,求两点之间的距离小于或等于K的点对个数。
找重心,相当于把无根树转变为有根树。再利用分治的思想(根据经过重心或不经过重心来划分)。
对于在同一个子树的情况要单独拎出来算并减去它们(因为是不该算在内的呀qwq)
另外选取重心的好处是时间复杂度O(NlogN*logN),如果选取其他的点,不能保证会不会退化到O(N2*logN)
//特别鸣谢,经@神犇gxz提醒,已改正刚才写时间复杂度的错误(大佬blog地址: http://www.cnblogs.com/GXZlegend/)
1 #include<cmath> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 const int maxn=10010; 6 using namespace std; 7 struct Edge 8 { 9 int nxt; 10 int to; 11 int w; 12 Edge(){} 13 Edge(int _to,int _nxt,int _w):to(_to),nxt(_nxt),w(_w){} 14 }edge[maxn<<1]; 15 int head[maxn],tot; 16 void init() 17 { 18 memset(head,-1,sizeof(head)); 19 tot=0; 20 } 21 22 void AddEdge(int u,int v,int w) 23 { 24 25 edge[tot]=Edge(v,head[u],w); 26 head[u]=tot++; 27 edge[tot]=Edge(u,head[v],w); 28 head[v]=tot++; 29 } 30 31 int son[maxn],siz[maxn],has[maxn],root; 32 int n,K; 33 void getroot(int u,int fa) 34 { 35 siz[u]=1;son[u]=0; 36 for(int i=head[u];i!=-1;i=edge[i].nxt) 37 { 38 int v = edge[i].to; 39 if(has[v]||v == fa) continue; 40 getroot(v,u); 41 siz[u] += siz[v]; 42 son[u] = max(son[u],siz[v]); 43 } 44 son[u] = max(son[u],son[0]-siz[u]); 45 if(son[u]<son[root]) root=u; 46 } 47 int dis[maxn],res[maxn]; 48 void getdis(int u,int fa) 49 { 50 res[++res[0]]=dis[u]; 51 siz[u]=1; 52 for(int i=head[u];i!=-1;i=edge[i].nxt) 53 { 54 int v=edge[i].to; 55 if(has[v]||v==fa) continue; 56 dis[v]=dis[u]+edge[i].w; 57 getdis(v,u); 58 siz[u]+=siz[v]; 59 } 60 } 61 int calc(int u,int init)//计算ans 62 { 63 dis[u]=init;res[0]=0;getdis(u,u); 64 sort(res+1,res+1+res[0]); 65 int l=1,r=res[0]; 66 int ans=0; 67 while(l<r) 68 { 69 if(res[l]+res[r]<=K) 70 ans+=r-l++; 71 else 72 --r; 73 } 74 return ans; 75 } 76 int Ans; 77 void dfs(int u) 78 { 79 Ans+=calc(u,0); 80 has[u]=1; 81 for(int i=head[u];i!=-1;i=edge[i].nxt) 82 { 83 int v=edge[i].to; 84 if(has[v]) continue; 85 Ans-=calc(v,edge[i].w); 86 root=0;son[0]=siz[v]; 87 getroot(v,v); 88 dfs(root); 89 } 90 } 91 int main() 92 { 93 while(~scanf("%d%d",&n,&K)) 94 { 95 int u,v,l; 96 if(n+K==0) break; 97 init(); 98 memset(has,0,sizeof(has)); 99 for(int i=0;i<n-1;i++) 100 { 101 scanf("%d%d%d",&u,&v,&l); 102 AddEdge(u,v,l); 103 } 104 root=0;son[0]=n; 105 getroot(1,1);//找重心 106 Ans=0; 107 dfs(root); 108 printf("%d\n",Ans); 109 } 110 return 0; 111 }
—————————————————————————————————————————————————
【poj 1987】
和1741基本是一样一样的,无脑复制就好啦つ﹏?
不过稍微注意一下,要记得用%*d或%*s跳过多余的输入。
1 #include<cmath> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 const int maxn=40010; 6 using namespace std; 7 struct Edge 8 { 9 int nxt; 10 int to; 11 int w; 12 Edge(){} 13 Edge(int _to,int _nxt,int _w):to(_to),nxt(_nxt),w(_w){} 14 }edge[maxn<<1]; 15 int head[maxn],tot; 16 void init() 17 { 18 memset(head,-1,sizeof(head)); 19 tot=0; 20 } 21 22 void AddEdge(int u,int v,int w) 23 { 24 25 edge[tot]=Edge(v,head[u],w); 26 head[u]=tot++; 27 edge[tot]=Edge(u,head[v],w); 28 head[v]=tot++; 29 } 30 31 int son[maxn],siz[maxn],has[maxn],root; 32 int n,K; 33 void getroot(int u,int fa) 34 { 35 siz[u]=1;son[u]=0; 36 for(int i=head[u];i!=-1;i=edge[i].nxt) 37 { 38 int v = edge[i].to; 39 if(has[v]||v == fa) continue; 40 getroot(v,u); 41 siz[u] += siz[v]; 42 son[u] = max(son[u],siz[v]); 43 } 44 son[u] = max(son[u],son[0]-siz[u]); 45 if(son[u]<son[root]) root=u; 46 } 47 int dis[maxn],res[maxn]; 48 void getdis(int u,int fa) 49 { 50 res[++res[0]]=dis[u]; 51 siz[u]=1; 52 for(int i=head[u];i!=-1;i=edge[i].nxt) 53 { 54 int v=edge[i].to; 55 if(has[v]||v==fa) continue; 56 dis[v]=dis[u]+edge[i].w; 57 getdis(v,u); 58 siz[u]+=siz[v]; 59 } 60 } 61 int calc(int u,int init)//计算ans 62 { 63 dis[u]=init;res[0]=0;getdis(u,u); 64 sort(res+1,res+1+res[0]); 65 int l=1,r=res[0]; 66 int ans=0; 67 while(l<r) 68 { 69 if(res[l]+res[r]<=K) 70 ans+=r-l++; 71 else 72 --r; 73 } 74 return ans; 75 } 76 int Ans; 77 void dfs(int u) 78 { 79 Ans+=calc(u,0); 80 has[u]=1; 81 for(int i=head[u];i!=-1;i=edge[i].nxt) 82 { 83 int v=edge[i].to; 84 if(has[v]) continue; 85 Ans-=calc(v,edge[i].w); 86 root=0;son[0]=siz[v]; 87 getroot(v,v); 88 dfs(root); 89 } 90 } 91 int main() 92 { 93 while(~scanf("%d%*d",&n)) 94 { 95 int u,v,l; 96 if(n+K==0) break; 97 init(); 98 memset(has,0,sizeof(has)); 99 for(int i=0;i<n-1;i++) 100 { 101 scanf("%d%d%d%*s",&u,&v,&l); 102 AddEdge(u,v,l); 103 } 104 scanf("%d",&K); 105 root=0;son[0]=n; 106 getroot(1,1); 107 Ans=0; 108 dfs(root); 109 printf("%d\n",Ans); 110 } 111 return 0; 112 }
—————————————————————————————————————————————————
【poj 2114】
题意:给定一棵边带权的树,求两点之间的距离刚好等于K的情况是否存在。
1 #include<cmath> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 const int maxn=10010; 6 using namespace std; 7 struct Edge 8 { 9 int nxt; 10 int to; 11 int w; 12 Edge(){} 13 Edge(int _to,int _nxt,int _w):to(_to),nxt(_nxt),w(_w){} 14 }edge[maxn<<1]; 15 int head[maxn],tot; 16 void init() 17 { 18 memset(head,-1,sizeof(head)); 19 tot=0; 20 } 21 22 void AddEdge(int u,int v,int w) 23 { 24 25 edge[tot]=Edge(v,head[u],w); 26 head[u]=tot++; 27 edge[tot]=Edge(u,head[v],w); 28 head[v]=tot++; 29 } 30 31 int son[maxn],siz[maxn],has[maxn],root; 32 int n,K; 33 void getroot(int u,int fa) 34 { 35 siz[u]=1;son[u]=0; 36 for(int i=head[u];i!=-1;i=edge[i].nxt) 37 { 38 int v = edge[i].to; 39 if(has[v]||v == fa) continue; 40 getroot(v,u); 41 siz[u] += siz[v]; 42 son[u] = max(son[u],siz[v]); 43 } 44 son[u] = max(son[u],son[0]-siz[u]); 45 if(son[u]<son[root]) root=u; 46 } 47 int dis[maxn],res[maxn]; 48 void getdis(int u,int fa) 49 { 50 res[++res[0]]=dis[u]; 51 siz[u]=1; 52 for(int i=head[u];i!=-1;i=edge[i].nxt) 53 { 54 int v=edge[i].to; 55 if(has[v]||v==fa) continue; 56 dis[v]=dis[u]+edge[i].w; 57 getdis(v,u); 58 siz[u]+=siz[v]; 59 } 60 } 61 int calc(int u,int init)//计算ans 62 { 63 dis[u]=init;res[0]=0;getdis(u,u); 64 sort(res+1,res+1+res[0]); 65 int l=1,r=res[0]; 66 int ans=0; 67 while (l<r) 68 { 69 if(res[l]+res[r]==K) 70 { 71 if (res[l]==res[r]) 72 { 73 ans+=(r-l+1)*(r-l)/2; 74 break; 75 } 76 int i=l,j=r; 77 while(res[l]==res[i]) ++i; 78 while(res[r]==res[j]) --j; 79 ans+=(i-l)*(r-j); 80 l=i,r=j; 81 } 82 else if (res[l]+res[r]>K) r--; 83 else l++; 84 } 85 return ans; 86 } 87 int Ans; 88 void dfs(int u) 89 { 90 has[u]=1; 91 Ans+=calc(u,0); 92 for(int i=head[u];i!=-1;i=edge[i].nxt) 93 { 94 int v=edge[i].to; 95 if(has[v]) continue; 96 Ans-=calc(v,edge[i].w); 97 root=0;son[0]=siz[v]; 98 getroot(v,v); 99 dfs(root); 100 } 101 } 102 103 int main() 104 { 105 while(~scanf("%d",&n)&&n) 106 { 107 int x,c; 108 init(); 109 for(int i=1;i<=n;i++) 110 { 111 while(~scanf("%d",&x)&&x) 112 { 113 scanf("%d",&c); 114 AddEdge(i,x,c); 115 // AddEdge(x,i,c);不用再反着存一遍了qwq 116 } 117 } 118 while(~scanf("%d",&K)&&K) 119 { 120 memset(has,0,sizeof(has)); 121 Ans=root=0; 122 son[0]=n; 123 getroot(1,1); 124 dfs(root); 125 printf(Ans?"AYE\n":"NAY\n"); 126 } 127 puts("."); 128 } 129 return 0; 130 } 131 /* 132 6 133 2 5 3 7 4 1 0 134 0 135 5 2 6 3 0 136 0 137 0 138 0 139 1 140 8 141 13 142 14 143 0 144 0 145 */
———————————————————————————————————————————————
/*
qwq就是这个题奇奇怪怪调了好久
跟机房dalao1号求助
dalao1号说这么长啊你给dalao2号吧
发给dalao2号
dalao2号看了一会儿说
我也看不出来你哪写错了你给dalao3号吧
于是发给dalao3号
dalao3号一看题目
愣了几秒说我可没写过点分治啊
……于是发给jdfz的dalao4号
dalao4号说你可以这样这样写
我按dalao4号的改了改发现还是TLE
实在没辙了qwq
最后我们家那口子某只Au爷说帮我看
过了几分钟……
Au爷:“来来来,我保证不打死你”
我:“啊啊啊不要打我啊我错了……”
被发现是把树存了4遍……
addedge函数里
我自己写了正着和反着加
结果main函数里读入时又存了两遍
所以才崩了啊啊啊
血与泪的教训
以后不能犯了(碎碎念qwq)
*/
poj1741+poj1987+poj2114——点分治入门题集合