首页 > 代码库 > 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 }
【poj1741】

—————————————————————————————————————————————————

【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 1987】

—————————————————————————————————————————————————

【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 */
【poj 2114】

———————————————————————————————————————————————

/*

qwq就是这个题奇奇怪怪调了好久

跟机房dalao1号求助

dalao1号说这么长啊你给dalao2号吧

发给dalao2号

dalao2号看了一会儿说

我也看不出来你哪写错了你给dalao3号吧

于是发给dalao3号

dalao3号一看题目

愣了几秒说我可没写过点分治啊

……于是发给jdfz的dalao4号

dalao4号说你可以这样这样写

我按dalao4号的改了改发现还是TLE

实在没辙了qwq

最后我们家那口子某只Au爷说帮我看

过了几分钟……

Au爷:“来来来,我保证不打死你”

我:“啊啊啊不要打我啊我错了……”

被发现是把树存了4遍……

addedge函数里

我自己写了正着和反着加

结果main函数里读入时又存了两遍

所以才崩了啊啊啊

血与泪的教训

以后不能犯了(碎碎念qwq)

*/

 

 

poj1741+poj1987+poj2114——点分治入门题集合