[配套题解] 点分治配套题解

这是 [点分治] 点分治入门 和 [模板] 点分治 的题解

入口:

[点分治] 点分治入门 : https://www.cnblogs.com/Railgun000/p/12597057.html

[模板] 点分治 : https://www.cnblogs.com/Railgun000/p/12810246.html

 

配套训练赛:https://vjudge.net/contest/362994#overview

 

题单:

POJ 1655

POJ 2114

POJ 1741

HDU 4812

HYSBZ 2152

HDU 5977

POJ 1987

 

题解:

 

A - Balancing Act

 POJ - 1655 

 

 1 #include<stdio.h>
 2 #include<iostream>
 3 #include<cstring>
 4 using namespace std;
 5 typedef long long ll;
 6 const int amn=1e5+5,inf=2e9;
 7 
 8 int n;
 9 
10 int head[amn],etot;
11 struct edge{
12     int nxt,v;
13     edge(){}
14     edge(int nxt,int v):nxt(nxt),v(v){}
15 }eg[amn];
16 void init(){
17     etot=0;
18     memset(head,0,sizeof head);
19 }
20 void add(int u,int v){
21     eg[++etot]=edge(head[u],v);
22     head[u]=etot;
23 }
24 
25 int siz[amn],maxt[amn];
26 void calsiz(int u,int fa,int sum){
27     siz[u]=1;
28     maxt[u]=0;
29     for(int i=head[u];i;i=eg[i].nxt){
30         int v=eg[i].v;
31         if(v==fa)continue;
32         calsiz(v,u,sum);
33         siz[u]+=siz[v];
34         maxt[u]=max(maxt[u],siz[v]);
35     }
36     maxt[u]=max(maxt[u],sum-siz[u]);
37 }
38 
39 int main(){
40     int t;scanf("%d",&t);
41     int u,v,ans1,ans2;
42     while(t--){
43         init();
44         scanf("%d",&n);
45         for(int i=1;i<n;i++){
46             scanf("%d%d",&u,&v);
47             add(u,v);
48             add(v,u);
49         }
50         calsiz(1,-1,n);
51         ans2=inf;
52         for(int i=1;i<=n;i++){
53             if(maxt[i]<ans2){
54                 ans1=i;
55                 ans2=maxt[i];
56             }
57         }
58         printf("%d %d\n",ans1,ans2);
59     }
60 }
61 
62 /**
63 题意:
64 给一个树,
65 去掉树上的一个节点,
66 求最大子树结点数的最小值,
67 如果有多个点都是最小值,
68 那么找一个序号最小的节点。
69 输出节点号,和最小值。
70 
71 分析:
72 求树的重心模板题,
73 1遍dfs求处每个结点的最大子树结点数,
74 为了使序号最小,则从结点1开始maxt数组找一个最小值.
75 注意这里有多组输入,则每次要初始化链式前向星的etot和head数组.
76 */

B - Boatherds

 POJ - 2114

 

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #include<queue>
  5 using namespace std;
  6 const int amn=1e5+5,inf=1e9;
  7 int n,m,K[amn];
  8 
  9 int head[amn],etot;
 10 struct edge{
 11     int nxt,v,w;
 12     edge(){}
 13     edge(int nxt,int v,int w):nxt(nxt),v(v),w(w){}
 14 }eg[amn];
 15 void init(){
 16     etot=0;
 17     memset(head,0,sizeof head);
 18 }
 19 void add(int u,int v,int w){
 20     eg[++etot]=edge(head[u],v,w);
 21     head[u]=etot;
 22 }
 23 
 24 int siz[amn],maxt[amn],vis[amn],rt;
 25 void calsiz(int u,int fa,int sum){
 26     siz[u]=1;
 27     maxt[u]=0;
 28     for(int i=head[u];i;i=eg[i].nxt){
 29         int v=eg[i].v;
 30         if(vis[v]||v==fa)continue;
 31         calsiz(v,u,sum);
 32         siz[u]+=siz[v];
 33         maxt[u]=max(maxt[u],siz[v]);
 34     }
 35     maxt[u]=max(maxt[u],sum-siz[u]);
 36     if(maxt[u]<maxt[rt])rt=u;
 37 }
 38 void getroot(int u,int fa,int sum){
 39     rt=0;
 40     maxt[rt]=inf;
 41     calsiz(u,fa,sum);
 42     calsiz(rt,-1,sum);
 43 }
 44 
 45 int dis[amn],di[amn],tp;
 46 void caldis(int u,int fa){
 47     if(dis[u]>(int)1e7)return;
 48     di[++tp]=dis[u];
 49     for(int i=head[u];i;i=eg[i].nxt){
 50         int v=eg[i].v,w=eg[i].w;
 51         if(vis[v]||v==fa)continue;
 52         dis[v]=dis[u]+w;
 53         caldis(v,u);
 54     }
 55 }
 56 
 57 bool jg[(int)1e7+5];
 58 int ans[amn];
 59 queue<int> bk;
 60 void sovle(int u){
 61     jg[0]=1;
 62     bk.push(0);
 63     for(int i=head[u];i;i=eg[i].nxt){
 64         int v=eg[i].v,w=eg[i].w;
 65         if(vis[v])continue;
 66         tp=0;
 67         dis[v]=w;
 68         caldis(v,u);
 69         for(int j=1;j<=tp;j++){
 70             for(int k=1;k<=m;k++){
 71                 if(K[k]>=di[j])ans[k]+=jg[K[k]-di[j]];
 72             }
 73         }
 74         for(int j=1;j<=tp;j++){
 75             jg[di[j]]=1;
 76             bk.push(di[j]);
 77         }
 78     }
 79     while(bk.size()){
 80         jg[bk.front()]=0;
 81         bk.pop();
 82     }
 83 }
 84 void dfz(int u){
 85     vis[u]=1;
 86     sovle(u);
 87     for(int i=head[u];i;i=eg[i].nxt){
 88         int v=eg[i].v;
 89         if(vis[v])continue;
 90         getroot(v,u,siz[v]);
 91         dfz(rt);
 92     }
 93 }
 94 int main(){
 95     int a,b;
 96     while(~scanf("%d",&n)&&n){
 97         init();
 98         for(int i=1;i<=n;i++){
 99             while(scanf("%d",&a)&&a){
100                 scanf("%d",&b);
101                 add(i,a,b);
102                 add(a,i,b);
103             }
104         }
105         m=0;
106         while(scanf("%d",&K[++m])&&K[m]);
107         m--;
108         memset(vis,0,sizeof vis);
109         memset(ans,0,sizeof ans);
110         getroot(1,-1,n);
111         dfz(rt);
112         for(int i=1;i<=m;i++){
113             if(ans[i])printf("AYE\n");
114             else printf("NAY\n");
115         }
116         puts(".");
117     }
118 }
119 /**
120 题意:
121 求一棵树上是否存在路径长度为K的点对。
122 
123 分析:
124 和ppt里的做法一样,只是输入和输出有区别
125 多组输入,注意初始化
126 每个case最后单独一行输出一个'.'
127 */

 

C - Tree

 POJ - 1741

 

  1 #include<stdio.h>
  2 #include<iostream>
  3 #include<queue>
  4 #include<string.h>
  5 #include<algorithm>
  6 using namespace std;
  7 typedef long long ll;
  8 const int amn=2e5+5,inf=2e9,top=2e4+5;
  9 
 10 int n,a,b,c,k;
 11 
 12 int head[amn],etot;
 13 struct edge{
 14     int nxt,v,w;
 15     edge(){}
 16     edge(int nxt,int v,int w):nxt(nxt),v(v),w(w){}
 17 }eg[amn];
 18 void add(int u,int v,int w){
 19     eg[++etot]=edge(head[u],v,w);
 20     head[u]=etot;
 21 }
 22 
 23 int siz[amn],maxt[amn],rt,vis[amn];
 24 void calsiz(int u,int fa,int sum){
 25     siz[u]=1;
 26     maxt[u]=0;
 27     for(int i=head[u];i;i=eg[i].nxt){
 28         int v=eg[i].v;
 29         if(vis[v]||v==fa)continue;
 30         calsiz(v,u,sum);
 31         siz[u]+=siz[v];
 32         maxt[u]=max(maxt[u],siz[v]);
 33     }
 34     maxt[u]=max(maxt[u],sum-siz[u]);
 35     if(maxt[u]<maxt[rt])rt=u;
 36 }
 37 void getroot(int u,int fa,int sum){
 38     rt=0;
 39     maxt[rt]=inf;
 40     calsiz(u,fa,sum);
 41     calsiz(rt,-1,sum);
 42 }
 43 
 44 int dis[amn],di[amn],tp;
 45 void caldis(int u,int fa){
 46     if(dis[u]>k)return ;    ///防溢出
 47     di[++tp]=dis[u];
 48     for(int i=head[u];i;i=eg[i].nxt){
 49         int v=eg[i].v,w=eg[i].w;
 50         if(vis[v]||v==fa)continue;
 51         dis[v]=dis[u]+w;
 52         caldis(v,u);
 53     }
 54 }
 55 
 56 int sovle(int u,int fa,int w){
 57     dis[u]=w;
 58     tp=0;///记得要初始化栈!!!
 59     caldis(u,fa);
 60     sort(di+1,di+1+tp);
 61     int l=1,r=tp,ans=0;
 62     while(l<r){
 63         if(di[l]+di[r]<=k){
 64             ans+=r-l;
 65             l++;
 66         }
 67         else r--;
 68     }
 69     return ans;
 70 }
 71 
 72 int ans;
 73 void dfz(int u){
 74     vis[u]=1;
 75     ans+=sovle(u,-1,0);
 76     for(int i=head[u];i;i=eg[i].nxt){
 77         int v=eg[i].v,w=eg[i].w;
 78         if(vis[v])continue;
 79         ans-=sovle(v,u,w);
 80         getroot(v,u,siz[v]);
 81         dfz(rt);
 82     }
 83 }
 84 
 85 void init(){
 86     etot=0;
 87     memset(head,0,sizeof head);
 88     ans=0;
 89     memset(vis,0,sizeof vis);///每次要初始化标记数组,这个要注意,多组输入的题要拿相同数据多试几遍看输出是否正常
 90 }
 91 
 92 int main(){
 93     while(~scanf("%d%d",&n,&k)&&n&&k){
 94         init();
 95         for(int i=1;i<n;i++){
 96             scanf("%d%d%d",&a,&b,&c);
 97             add(a,b,c);
 98             add(b,a,c);
 99         }
100         getroot(1,-1,n);
101         dfz(rt);
102         printf("%d\n",ans);
103     }
104 }
105 /**
106 题意:
107 求一棵树上路径长度大于等于1且小于等于K的点对个数。
108 
109 分析:
110 和ppt里的做法一样,这里选择双指针法
111 多组输入,注意初始化
112 */

 

D - D Tree

 HDU - 4812

 

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #include<queue>
  5 using namespace std;
  6 const int amn=5e5+5,inf=2e9,mod=1e6+3;
  7 typedef long long ll;
  8 
  9 int n,ansx,ansy;
 10 ll V[amn],inv[(int)(mod+5)],K;
 11 
 12 int head[amn],etot;
 13 struct edge{
 14     int to,v;
 15     edge(){}
 16     edge(int to,int v):to(to),v(v){}
 17 }eg[amn];
 18 void add(int u,int v){
 19     eg[++etot]=edge(head[u],v);
 20     head[u]=etot;
 21 }
 22 
 23 int siz[amn],maxt[amn],rt,vis[amn];
 24 void calsiz(int u,int fa,int sum){
 25     siz[u]=1;
 26     maxt[u]=0;
 27     for(int i=head[u];i;i=eg[i].to){
 28         int v=eg[i].v;
 29         if(vis[v]||v==fa)continue;
 30         calsiz(v,u,sum);
 31         siz[u]+=siz[v];
 32         maxt[u]=max(maxt[u],siz[v]);
 33     }
 34     maxt[u]=max(maxt[u],sum-siz[u]);
 35     if(maxt[u]<maxt[rt])rt=u;
 36 }
 37 void getroot(int u,int fa,int sum){
 38     rt=0;
 39     maxt[rt]=inf;
 40     calsiz(u,fa,sum);
 41 }
 42 
 43 ll dis[amn],di[amn],tp;
 44 void caldis(int u,int fa){
 45     di[++tp]=u;
 46     for(int i=head[u];i;i=eg[i].to){
 47         int v=eg[i].v;
 48         if(vis[v]||v==fa)continue;
 49         dis[v]=V[v]*dis[u]%mod;
 50         caldis(v,u);
 51     }
 52 }
 53 
 54 int tf[(int)(mod+5)];
 55 queue<int> bk;
 56 void sovle(int u){
 57     tf[1]=u;
 58     bk.push(1);
 59     for(int i=head[u];i;i=eg[i].to){
 60         int v=eg[i].v;
 61         if(vis[v])continue;
 62         tp=0;
 63         dis[v]=V[v];
 64         caldis(v,u);
 65         for(int j=1;j<=tp;j++){
 66             ll x=di[j],y=tf[K*inv[dis[x]*V[u]%mod]%mod];
 67             if(!y)continue;
 68             if(x>y)swap(x,y);
 69             if(x<ansx||(x==ansx&&y<ansy))
 70                 ansx=x,ansy=y;
 71         }
 72         for(int j=1;j<=tp;j++){
 73             if(!tf[dis[di[j]]]||di[j]<tf[dis[di[j]]]){
 74                 tf[dis[di[j]]]=di[j];
 75                 bk.push(dis[di[j]]);
 76             }
 77         }
 78     }
 79     while(bk.size()){
 80         tf[bk.front()]=0;
 81         bk.pop();
 82     }
 83 }
 84 
 85 void dfz(int u){
 86     vis[u]=1;
 87     sovle(u);
 88     for(int i=head[u];i;i=eg[i].to){
 89         int v=eg[i].v;
 90         if(vis[v])continue;
 91         getroot(v,u,siz[v]);
 92         dfz(rt);
 93     }
 94 }
 95 
 96 void init(int n){
 97     ansx=ansy=inf;
 98     etot=0;
 99     memset(head,0,sizeof head);
100     memset(vis,0,sizeof vis);
101 }
102 
103 int main(){
104     inv[1]=1;
105     for(int i=2;i<=mod;i++)
106         inv[i]=(1LL*(-(mod/i)*inv[mod%i]%mod+mod)%mod);
107     while(~scanf("%d%d",&n,&K)){
108         init(n);
109         for(int i=1;i<=n;i++){
110             scanf("%lld",&V[i]);
111         }
112         for(int i=1;i<n;i++){
113             int x,y;
114             scanf("%d%d",&x,&y);
115             add(x,y);
116             add(y,x);
117         }
118         getroot(1,-1,n);
119         dfz(rt);
120         if(ansx==inf)printf("No solution\n");
121         else printf("%d %d\n",ansx,ansy);
122     }
123 }
124 /**
125 题意:
126 在树上找一条链,使得链上点权值的乘积对1e6+3取模为k,输出两个端点,若有相同情况输出字典序最小的编号.
127 
128 分析:
129 
130 dis[x]*dis[y]%mod==K
131         |
132         v
133 dis[y]==K*inv[dis[x]]%mod
134 
135 用类似判断是否有路径和等于k的方法
136 caldis函数的di数组改为统计端点的编号,
137 solve函数的tf数组改为存端点的编号
138 */

 

E - 聪聪可可

 HYSBZ - 2152

 

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 typedef long long ll;
  4 const int amn=1e5+5,inf=1e9;
  5 
  6 int n,a,b,c;
  7 
  8 int head[amn],egnum;
  9 struct edge{
 10     int nxt,v,w;
 11     edge(){}
 12     edge(int nxt,int v,int w):nxt(nxt),v(v),w(w){}
 13 }eg[amn];
 14 void add(int u,int v,int w){
 15     eg[++egnum]=edge(head[u],v,w);
 16     head[u]=egnum;
 17 }
 18 
 19 int siz[amn],maxt[amn],rt,vis[amn];
 20 void calsiz(int u,int fa,int sum){
 21     siz[u]=1;
 22     maxt[u]=0;
 23     for(int i=head[u];i;i=eg[i].nxt){
 24         int v=eg[i].v;
 25         if(vis[v]||v==fa)continue;
 26         calsiz(v,u,sum);
 27         siz[u]+=siz[v];
 28         maxt[u]=max(maxt[u],siz[v]);
 29     }
 30     maxt[u]=max(maxt[u],sum-siz[u]);
 31     if(maxt[u]<maxt[rt])rt=u;
 32 }
 33 void getroot(int u,int fa,int sum){
 34     rt=0;
 35     maxt[rt]=inf;
 36     calsiz(u,fa,sum);
 37     calsiz(rt,-1,sum);
 38 }
 39 
 40 int dis[amn],di[amn],tp;
 41 void caldis(int u,int fa){
 42     di[++tp]=dis[u];
 43     for(int i=head[u];i;i=eg[i].nxt){
 44         int v=eg[i].v,w=eg[i].w;
 45         if(vis[v]||v==fa)continue;
 46         dis[v]=dis[u]+w;
 47         caldis(v,u);
 48     }
 49 }
 50 
 51 int jg[5],ans;
 52 void solve(int u){
 53     for(int i=head[u];i;i=eg[i].nxt){
 54         int v=eg[i].v,w=eg[i].w;
 55         if(vis[v])continue;
 56         tp=0;
 57         dis[v]=w;
 58         caldis(v,u);
 59         for(int j=1;j<=tp;j++){
 60             ans+=2*((di[j]%3==0?1:0)+jg[(3-di[j]%3+3)%3]);
 61         }
 62         for(int j=1;j<=tp;j++){
 63             jg[di[j]%3]++;
 64         }
 65     }
 66     jg[0]=jg[1]=jg[2]=0;
 67 }
 68 void dfz(int u){
 69     vis[u]=1;
 70     solve(u);
 71     for(int i=head[u];i;i=eg[i].nxt){
 72         int v=eg[i].v,w=eg[i].w;
 73         if(vis[v])continue;
 74         getroot(v,u,siz[v]);
 75         dfz(rt);
 76     }
 77 }
 78 
 79 int gcd(int a,int b){
 80     return b?gcd(b,a%b):a;
 81 }
 82 
 83 int main(){
 84     scanf("%d",&n);
 85     for(int i=1;i<n;i++){
 86         scanf("%d%d%d",&a,&b,&c);
 87         add(a,b,c);
 88         add(b,a,c);
 89     }
 90     ans=n;
 91     getroot(1,-1,n);
 92     dfz(rt);
 93     int fm=n*n;
 94     int g=gcd(ans,fm);
 95     printf("%d/%d\n",ans/g,fm/g);
 96 }
 97 /**
 98 题意:
 99 给一颗树,有n给点,两个点之间所有边上数的和加起来恰好是3的倍数的概率,用最简分数形式输出
100 
101 分析:
102 求出两个点之间所有边上数的和加起来恰好是3的倍数的数量,和所有路径数n*n求gcd即可
103 所以问题是如何求路径距离为3的倍数的数量
104 如果一个数a是3的倍数,则a%3==0
105 如果数b和c不是3的倍数,则b%3==1,c%3==2
106 那么3的倍数有下列情况:a%3==0,(a+a)%3==0,(b+c)%3==0
107 按照求路径距离为k的个数的方法改下sovle函数就行了
108 注意这里是有向路径,而我们只算了一个方向,所以中累加答案时要乘2
109 */

 

F - Garden of Eden

 HDU - 5977

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 using namespace std;
  5 typedef long long ll;
  6 const int amn=1e5+5,inf=2e9;
  7 
  8 int n,k;
  9 ll ans;
 10 
 11 int head[amn],egnum,type[amn];
 12 struct edge{
 13     int to,v;
 14     edge(){}
 15     edge(int to,int v):to(to),v(v){}
 16 }eg[amn];
 17 void add(int u,int v){
 18     eg[++egnum]=edge(head[u],v);
 19     head[u]=egnum;
 20 }
 21 void add_eg(int u,int v){
 22     add(u,v);
 23     add(v,u);
 24 }
 25 
 26 int siz[amn],maxt[amn],rt;bool vis[amn];
 27 void calsiz(int u,int fa,int sum){
 28     siz[u]=1;
 29     maxt[u]=0;
 30     for(int i=head[u];i;i=eg[i].to){
 31         int v=eg[i].v;
 32         if(v==fa||vis[v])continue;
 33         calsiz(v,u,sum);
 34         siz[u]+=siz[v];
 35         maxt[u]=max(maxt[u],siz[v]);
 36     }
 37     maxt[u]=max(maxt[u],sum-siz[u]);
 38     if(maxt[u]<maxt[rt])rt=u;
 39 }
 40 void getroot(int u,int fa,int sum){
 41     rt=0;
 42     maxt[rt]=inf;
 43     calsiz(u,fa,sum);
 44 }
 45 
 46 ll di[amn],tp,cnt;
 47 void caldis(int u,int fa,ll cnt){
 48     di[++tp]=cnt;
 49     for(int i=head[u];i;i=eg[i].to){
 50         int v=eg[i].v;
 51         if(v==fa||vis[v])continue;
 52         caldis(v,u,cnt|(1<<(type[v]-1)));
 53     }
 54 }
 55 ll tf[3000];
 56 ll tot;
 57 ll sovle(int u,int fa){
 58     ll an=0;
 59     tp=0;
 60     caldis(u,fa,(1<<(type[u]-1))|(1<<(type[fa]-1)));
 61     for(int i=1;i<=tp;i++)tf[di[i]]++;  ///处理了所有路径情况
 62     for(int i=1;i<=tp;i++){
 63         tf[di[i]]--;    ///这里是当前路径与其他路径组合,所以要减掉自身
 64         an+=tf[tot];
 65         for(int j=di[i];j;j=((j-1)&di[i]))an+=tf[tot^j];    ///枚举di[i]状态的子集
 66         tf[di[i]]++;    ///还原自身
 67     }
 68     for(int i=1;i<=tp;i++){
 69         tf[di[i]]=0;
 70     }
 71     return an;
 72 }
 73 void dfz(int u){
 74     vis[u]=1;
 75     ans+=sovle(u,u);
 76     for(int i=head[u];i;i=eg[i].to){
 77         int v=eg[i].v;
 78         if(vis[v])continue;
 79         ans-=sovle(v,u);        ///sovle函数算了rt结点,所以要减掉不合法的情况,详情见ppt双指针的内容
 80         getroot(v,u,siz[v]);
 81         dfz(rt);
 82     }
 83 }
 84 void init(int n){
 85     ans=egnum=0;
 86     for(int i=1;i<=n;i++)head[i]=vis[i]=0;
 87     tot=(1<<k)-1;
 88 }
 89 
 90 int main(){
 91     while(~scanf("%d%d",&n,&k)){
 92         init(n);
 93         for(int i=1;i<=n;i++){
 94             scanf("%d",&type[i]);
 95         }
 96         int u,v;
 97         for(int i=1;i<n;i++){
 98 
 99             scanf("%d%d",&u,&v);
100             add_eg(u,v);
101         }
102         if(k==1){printf("%d\n",n*n);continue;}
103         getroot(1,-1,n);
104         dfz(rt);
105         printf("%lld\n",ans);
106     }
107 }
108 /**
109 题意:
110 给一棵树,树上有K种苹果,
111 问从任意点出发,到任意点结束,
112 把路径上的所有苹果种类取全,
113 能够取全K种苹果的有向点对数(1->2和2->1是不同的)
114 
115 分析:
116 我们需要记录每个点到根节点的路径上取到的苹果的种类
117 再看这条路径是否把苹果种类取全,
118 或它能与其他子树的哪些路径互补使得这条组合路径把苹果种类取全
119 注意,如果一条路径把苹果取全后,那么它能与任意其他子树的哪些路径互补使得这条组合路径把苹果种类取全
120 因为它本来就是种类齐全的
121 如果用二进制表示,就像这样( | 是或运算):1111 | 0001 = 1111, 1111 | 0011 = 1111,
122 这就启发了我们使用二进制来表示路径取苹果种类的状态,
123 每条路径状态都可以与全1状态互补为全1状态,每条路径算答案时都要加上当前全1状态路径数
124 
125 我们知道,^是异或运算,a^b==c , b==c^a ,也就是说,已知c和a就可以通过异或求出b,
126 这就启发了我们用异或来查询其他子树的路径状态数
127 
128 由前面分析的种类齐全的情况,
129 启发了我们如果得到一个路径状态a,
130 那么a的子集也由可能与其他子树的路径互补为全1状态
131 */

G - Distance Statistics

 POJ - 1987

  1 #include<stdio.h>
  2 #include<iostream>
  3 #include<queue>
  4 #include<string.h>
  5 #include<algorithm>
  6 using namespace std;
  7 typedef long long ll;
  8 const int amn=2e5+5,inf=2e9,top=2e4+5;
  9 
 10 int n,m,a,b,c,k;
 11 
 12 int head[amn],etot;
 13 struct edge{
 14     int nxt,v,w;
 15     edge(){}
 16     edge(int nxt,int v,int w):nxt(nxt),v(v),w(w){}
 17 }eg[amn];
 18 void add(int u,int v,int w){
 19     eg[++etot]=edge(head[u],v,w);
 20     head[u]=etot;
 21 }
 22 
 23 int siz[amn],maxt[amn],rt,vis[amn];
 24 void calsiz(int u,int fa,int sum){
 25     siz[u]=1;
 26     maxt[u]=0;
 27     for(int i=head[u];i;i=eg[i].nxt){
 28         int v=eg[i].v;
 29         if(vis[v]||v==fa)continue;
 30         calsiz(v,u,sum);
 31         siz[u]+=siz[v];
 32         maxt[u]=max(maxt[u],siz[v]);
 33     }
 34     maxt[u]=max(maxt[u],sum-siz[u]);
 35     if(maxt[u]<maxt[rt])rt=u;
 36 }
 37 void getroot(int u,int fa,int sum){
 38     rt=0;
 39     maxt[rt]=inf;
 40     calsiz(u,fa,sum);
 41     calsiz(rt,-1,sum);
 42 }
 43 
 44 int dis[amn],di[amn],tp;
 45 void caldis(int u,int fa){
 46     if(dis[u]>k)return ;    ///防溢出
 47     di[++tp]=dis[u];
 48     for(int i=head[u];i;i=eg[i].nxt){
 49         int v=eg[i].v,w=eg[i].w;
 50         if(vis[v]||v==fa)continue;
 51         dis[v]=dis[u]+w;
 52         caldis(v,u);
 53     }
 54 }
 55 
 56 int sovle(int u,int fa,int w){
 57     dis[u]=w;
 58     tp=0;///记得要初始化栈!!!
 59     caldis(u,fa);
 60     sort(di+1,di+1+tp);
 61     int l=1,r=tp,ans=0;
 62     while(l<r){
 63         if(di[l]+di[r]<=k){
 64             ans+=r-l;
 65             l++;
 66         }
 67         else r--;
 68     }
 69     return ans;
 70 }
 71 
 72 int ans;
 73 void dfz(int u){
 74     vis[u]=1;
 75     ans+=sovle(u,-1,0);
 76     for(int i=head[u];i;i=eg[i].nxt){
 77         int v=eg[i].v,w=eg[i].w;
 78         if(vis[v])continue;
 79         ans-=sovle(v,u,w);
 80         getroot(v,u,siz[v]);
 81         dfz(rt);
 82     }
 83 }
 84 
 85 void init(){
 86     etot=0;
 87     memset(head,0,sizeof head);
 88     ans=0;
 89     memset(vis,0,sizeof vis);///每次要初始化标记数组,这个要注意,多组输入的题要拿相同数据多试几遍看输出是否正常
 90 }
 91 
 92 int main(){
 93     char in;
 94     while(~scanf("%d%d",&n,&m)){
 95         init();
 96         for(int i=1;i<=m;i++){
 97             scanf("%d%d%d %c",&a,&b,&c,&in);
 98             add(a,b,c);
 99             add(b,a,c);
100         }
101         scanf("%d",&k);
102         getroot(1,-1,n);
103         dfz(rt);
104         printf("%d\n",ans);
105     }
106 }
107 /**
108 题意:
109 求一棵树上路径长度大于等于1且小于等于K的点对个数。
110 
111 分析:
112 和ppt里的做法一样,这里选择双指针法
113 多组输入,注意初始化
114 和poj1741不同的是K (1 <= K <= 1,000,000,000)变为最大到1e9了,输入也有不同
115 */
posted @ 2020-04-30 18:08  Railgun000  阅读(170)  评论(0编辑  收藏  举报