1 /*解法:二分+最短路:这里的最短路是抽象意义上的最短路,dis表示的从1--i需要连几条线,所以我们二分一个“最长的电话线的长度x”,
当电话线长度>x,就+1进行转移,否则+0进行转移,同时记录路径上是否刚好有权值为x的边*/
2 #include<iostream>
3 using namespace std;
4 #define N 1001
5 #include<cstdio>
6 #include<cstring>
7 #define P 100010
8 struct Edge{
9 int v,last,w;
10 }edge[P*2];
11 int head[N],t=0;
12 bool visit[N];
13 int dis[N],pre_path[N];
14 int n,p,k;
15 inline int read()
16 {
17 int ans=0;
18 char s;
19 s=getchar();
20 while(s<‘0‘||s>‘9‘) s=getchar();
21 while(‘0‘<=s&&s<=‘9‘)
22 {
23 ans=ans*10+s-‘0‘;
24 s=getchar();
25 }
26 return ans;
27 }
28 void add_edge(int u,int v,int w)
29 {
30 ++t;
31 edge[t].v=v;
32 edge[t].w=w;
33 edge[t].last=head[u];
34 head[u]=t;
35 }
36 void input(int &maxx)
37 {
38 n=read();p=read();k=read();
39 for(int i=1;i<=p;++i)
40 {
41 int a,b,c;
42 a=read();b=read();c=read();
43 add_edge(a,b,c);
44 add_edge(b,a,c);
45 maxx=max(maxx,c);
46 }
47 }
48 int dijkstra(int x,bool <)/*dijikstra算法求最短路径*/
49 {
50 memset(pre_path,0,sizeof(pre_path));
51 memset(visit,false,sizeof(visit));
52 memset(dis,99,sizeof(dis));
53 bool flag=false;
54 dis[1]=0;
55 for(int i=1;i<n;++i)
56 {
57 int maxx=(1<<31)-1,p;
58 for(int i=1;i<=n;++i)
59 if(!visit[i]&&dis[i]<maxx)
60 {
61 maxx=dis[i];p=i;
62 }
63 visit[p]=true;
64 for(int l=head[p];l;l=edge[l].last)
65 {
66 if(!visit[edge[l].v])
67 {
68 if(edge[l].w>x)/*边权>x,+1转移*/
69 {
70 if(dis[edge[l].v]>dis[p]+1)
71 {
72 pre_path[edge[l].v]=p;
73 dis[edge[l].v]=dis[p]+1;
74 }
75 }
76 else {
77 if(dis[edge[l].v]>dis[p])
78 {
79 pre_path[edge[l].v]=p;
80 dis[edge[l].v]=dis[p];
81 }
82 }
83 }
84 }
85 }
86 if(dis[n]==1667457891) lt=false;/*表是1--n不连通*/
87 if(dis[n]>k)return -1;/*连的边多了,说明*/
88 int i=n;
89 /*记录路径的目的是判断x是不是刚好是路径上边的权值,注意不能在dijkstra中判断权值是不是等于x,因为更新的边不一定是最终路径中的边*/
90 while(pre_path[i])
91 {
92 int j=pre_path[i];
93 for(int l=head[j];l;l=edge[l].last)
94 {
95 if(edge[l].v==i&&edge[l].w==x)
96 {
97 flag=true;break;
98 }
99 }
100 if(flag) break;
101 i=pre_path[i];
102 }
103 if(flag) return 0;/*能取到x,返回0*/
104 return 1;
105 }
106 int find_ans(int l,int r)
107 {
108 bool lt=true;/*表示1--n是否连通*/
109 int ans,mid;
110 while(l<=r)
111 {
112 mid=(l+r)>>1;
113 int temp=dijkstra(mid,lt);
114 if(temp==-1) l=mid+1;
115 if(temp==0) ans=mid,r=mid-1;
116 if(temp==1) r=mid-1;
117 if(!lt) return -1;
118 }
119 return ans;
120 }
121 int main()
122 {
123 int maxx=0;
124 input(maxx);/*确定二分范围*/
125 printf("%d\n",find_ans(0,maxx));
126 return 0;
127 }
公元 2044 年,人类进入了宇宙纪元。L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球。小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之间不会产生任何干扰。为了鼓励科技创新, L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。在虫洞的建设完成前小 P 的物流公司就预接了 m 个运输计划。在虫洞建设完成后,这 m 个运输计划会同时开始,所有飞船一起出发。当这 m 个运输计划都完成时,小 P 的物流公司的阶段性工作就完成了。如果小 P 可以自由选择将哪一条航道改造成虫洞, 试求出小 P 的物流公司完成阶段性工作所需要的最短时间是多少?
Input
第一行包括两个正整数 n,m,表示 L 国中星球的数量及小 P 公司预接的运输计划的数量,星球从 1 到 n 编号。接下来 n−1 行描述航道的建设情况,其中第 i 行包含三个整数 ai,bi 和 ti,表示第 i 条双向航道修建在 ai 与 bi 两个星球之间,任意飞船驶过它所花费的时间为 ti。数据保证 1≤ai,bi≤n 且 0≤ti≤1000。接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 uj 和 vj,表示第 j 个运输计划是从 uj 号星球飞往 vj号星球。数据保证 1≤ui,vi≤n
输出文件只包含一个整数,表示小 P 的物流公司完成阶段性工作所需要的最短时间。
将第 1 条航道改造成虫洞: 则三个计划耗时分别为:11,12,11,故需要花费的时间为 12。
将第 2 条航道改造成虫洞: 则三个计划耗时分别为:7,15,11,故需要花费的时间为 15。
将第 3 条航道改造成虫洞: 则三个计划耗时分别为:4,8,11,故需要花费的时间为 11。
将第 4 条航道改造成虫洞: 则三个计划耗时分别为:11,15,5,故需要花费的时间为 15。
将第 5 条航道改造成虫洞: 则三个计划耗时分别为:11,10,6,故需要花费的时间为 11。
故将第 3 条或第 5 条航道改造成虫洞均可使得完成阶段性工作的耗时最短,需要花费的时间为 11。
我的错误代码:
1 /*代码写的巨长,不仅爆了空间,结果还不对,直接绝望了。
2 没有处理好的两个地方:1.储存lca的路径 2.枚举删除那条边的过程,
3 我都是用了巨耗空间和时间的方法,直接不忍直视!
4 */
5 #include<iostream>
6 using namespace std;
7 #include<cstdio>
8 #include<cstring>
9 #define N 300010
10 #include<vector>
11 vector<int> ques[N];
12 vector<int> path[N];
13 int father[N],ance[N];
14 int maxx=0;
15 bool LCA[N]={false};
16 struct Edge{
17 int u,v,last,ti;
18 }edge[N*3];
19 int ans[N];
20 int q;
21 int head[N],dis[N],t=0;
22 bool root[N]={false};
23 int cnt[N];//n ge bian
24 int contflag[N]={false};
25 int n,m,a,b,ti;
26 int read()
27 {
28 int ans=0;char s;
29 s=getchar();
30 while(s<‘0‘||s>‘9‘) s=getchar();
31 while(‘0‘<=s&&s<=‘9‘)
32 {
33 ans=ans*10+s-‘0‘;
34 s=getchar();
35 }
36 return ans;
37 }
38 void add_edge(int u,int v,int w,int p)
39 {
40 edge[p].u=u;
41 edge[p].v=v;
42 edge[p].ti=w;
43 edge[p].last=head[u];
44 head[u]=p;
45 }
46 void input()
47 {
48 n=read();m=read();
49 for(int i=1;i<n;++i)
50 {
51 a=read();b=read();ti=read();
52 add_edge(a,b,ti,i);
53 root[b]=true;
54 father[i]=i;
55 }
56 father[n]=n;
57 q=n-1;
58 for(int i=1;i<=m;++i)
59 {
60 a=read();b=read();
61 ques[a].push_back(i);
62 ques[a].push_back(b);
63 ques[b].push_back(i);
64 ques[b].push_back(a);
65 q++;
66 add_edge(a,b,0,q);
67 }
68 }
69 int find(int x)
70 {
71 return father[x]==x?father[x]:father[x]=find(father[x]);
72 }
73 void tarjan(int k,int w,int p)
74 {
75 dis[k]=w;
76 ance[k]=k;
77 path[k].push_back(p);
78 int size=path[k].size();
79 for(int l=head[k];l;l=edge[l].last)
80 {
81 for(int i=0;i<size;++i)
82 path[edge[l].v].push_back(path[k][i]);
83 tarjan(edge[l].v,w+edge[l].ti,l);
84 father[edge[l].v]=k;
85 ance[edge[l].v]=k;
86 }
87 LCA[k]=true;
88 int sze=ques[k].size();
89 for(int i=1;i<sze;i+=2)
90 {
91 if(LCA[ques[k][i]])
92 {
93 int zu=ance[find(ques[k][i])];
94 ans[ques[k][i-1]]=dis[k]+dis[ques[k][i]]-2*dis[zu];
95 maxx=max(maxx,ans[ques[k][i-1]]);
96 }
97 }
98 }
99 int check(int x)
100 {
101 int flag[1001]={0};
102 memset(contflag,false,sizeof(contflag));
103 int cont=m;
104 memset(cnt,0,sizeof(cnt));
105 for(int i=n;i<=q;++i)
106 {
107 int sum=ans[i-(n-1)];
108 int u=edge[i].u;
109 int v=edge[i].v;
110 int sze=path[u].size();
111 for(int i=0;i<sze;++i)
112 {
113 if(sum>x&&sum-edge[path[u][i]].ti<=x)
114 {
115 if(sum-edge[path[u][i]].ti==x) flag[++flag[0]]=path[u][i];
116 cnt[path[u][i]]++;
117 if(!contflag[i])
118 {
119 contflag[i]=true;
120 cont--;
121 }
122 }
123 }
124 sze=path[v].size();
125 for(int i=0;i<sze;++i)
126 {
127 if(sum>x&&sum-edge[path[v][i]].ti<=x)
128 {
129 if(sum-edge[path[v][i]].ti==x) flag[++flag[0]]=path[v][i];
130 cnt[path[v][i]]++;
131 if(!contflag[i])
132 {
133 contflag[i]=true;
134 cont--;
135 }
136 }
137 }
138 }
139 int biaozhi=false;
140 int qq=0;
141 for(int i=1;i<n;++i)
142 if(cnt[i]==m)
143 {
144 qq++;
145 for(int j=1;j<=flag[0];++j)
146 if(flag[j]==i)
147 {
148 biaozhi=true;break;
149 }
150 }
151 if(qq==0||cont>0) return -1;
152 if(qq>=1&&biaozhi) return 0;
153 return 1;
154 }
155 int find_answe(int l,int r)
156 {
157 int answe,mid;
158 int temp;
159 while(l<=r)
160 {
161 mid=(l+r)>>1;
162 int temp=check(mid);
163 if(temp==-1) l=mid+1;
164 if(temp==0) answe=mid,r=mid-1;
165 if(temp==1) r=mid-1;
166 }
167 return answe;
168 }
169 int main()
170 {
171 input();
172 for(int i=1;i<=n;++i)
173 if(!root[i])
174 {
175 tarjan(i,0,0);
176 break;
177 }
178 printf("%d\n",find_answe(0,maxx));
179 return 0;
180 }
正确代码(不知道为什么这个代码在BZOJ上可以过,在codevs上最后一个点就超时):
1 /*真是一道好题,lca+二分+树上标记*/
2 #include<iostream>
3 using namespace std;
4 #define N 300010
5 #include<vector>
6 #include<cstring>
7 #include<cstdio>
8 struct Edge{
9 int v,w,last;
10 }edge[N<<1];
11 struct LCA{
12 int a,b,zu,di;
13 }lca[N];
14 int n,m,t=0;
15 bool visit_lca[N]={false};
16 vector<int> que[N];
17 int head[N],dis[N],father[N],fa[N],ance[N],sum[N],maxx=-1,e[N];
18 int read()
19 {
20 int ans=0;char s;
21 s=getchar();
22 while(s<‘0‘||s>‘9‘) s=getchar();
23 while(‘0‘<=s&&s<=‘9‘)
24 {
25 ans=ans*10+s-‘0‘;
26 s=getchar();
27 }
28 return ans;
29 }
30 void add_edge(int u,int v,int w)
31 {
32 ++t;
33 edge[t].v=v;
34 edge[t].w=w;
35 edge[t].last=head[u];
36 head[u]=t;
37 }
38 void input()
39 {
40 int a,b,ti;
41 n=read();m=read();
42 for(int i=1;i<n;++i)
43 {
44 a=read();
45 b=read();
46 ti=read();
47 add_edge(a,b,ti);
48 add_edge(b,a,ti);
49 }
50 for(int i=1;i<m+1;++i)
51 {
52 lca[i].a=read();
53 lca[i].b=read();
54 que[lca[i].a].push_back(i);
55 que[lca[i].a].push_back(lca[i].b);
56 que[lca[i].b].push_back(i);
57 que[lca[i].b].push_back(lca[i].a);
58 }
59 }
60 int find(int x)
61 {
62 return (father[x]==x)?father[x]:father[x]=find(father[x]);
63 }
64 void tarjan(int k,int w1)
65 {
66 ance[k]=k;
67 father[k]=k;
68 dis[k]=w1;
69 for(int l=head[k];l;l=edge[l].last)
70 {
71 if(edge[l].v!=fa[k])
72 {
73 fa[edge[l].v]=k;
74 e[edge[l].v]=l;
75 tarjan(edge[l].v,w1+edge[l].w);
76 father[edge[l].v]=k;
77 ance[edge[l].v]=k;
78 }
79 }
80 visit_lca[k]=true;
81 int sze=que[k].size();
82 for(int i=1;i<sze;i+=2)
83 {
84 int v=que[k][i];
85 if(visit_lca[v])
86 {
87 lca[que[k][i-1]].zu=ance[find(v)];
88 lca[que[k][i-1]].di=dis[k]+dis[v]-2*dis[lca[que[k][i-1]].zu];
89 maxx=max(maxx,lca[que[k][i-1]].di);
90 }
91 }
92 }
93 void dfs(int x)
94 {
95 for(int l=head[x];l;l=edge[l].last)
96 {
97 if(edge[l].v!=fa[x])
98 {
99 dfs(edge[l].v);
100 sum[x]+=sum[edge[l].v];/*标记上传*/
101 }
102 }
103 }
104 int check(int x)
105 {
106 int tot=0,dec=0;
107 bool biaozhi=false;
108 memset(sum,0,sizeof(sum));
109 for(int i=1;i<=m;++i)
110 {
111 if(lca[i].di>x)/*只记录不符合要求的边*/
112 {
113 dec=max(dec,lca[i].di-x);/*至少需要删的边的长度*/
114 sum[lca[i].a]++;
115 sum[lca[i].b]++;
116 sum[lca[i].zu]-=2;
117 tot++;
118 }
119 if(lca[i].di==x) biaozhi=true;
120 }
121 dfs(1);
122 int flag=false;
123 for(int i=1;i<=n;++i)
124 if(sum[i]==tot&&edge[e[i]].w>=dec)
125 {/*sum[i]==tot,表示在这tot条路径上的公共点,只有这些边删了才有意义*/
126 if(edge[e[i]].w==dec) biaozhi=true;
127 flag=true;
128 break;
129 }
130 /*这里的逻辑关系一开始没有分析好,biaozhi表示可以取到x,(包括两种情况,一是本来就有lca[i].di==x,二是删边之后达到了x的状态,这两种都要考虑得到),flag表示能不能删边达到<=x的状态*/
131 if(flag&&biaozhi)return 0;
132 if(flag) return 1;
133 return -1;
134 }
135 int find_ans(int l,int r)
136 {
137 int mid,ans=0;
138 while(l<=r)
139 {
140 mid=(l+r)>>1;
141 int temp=check(mid);
142 if(temp==-1)
143 l=mid+1;//zhao bu dao
144 if(temp==0)
145 ans=mid,r=mid-1;
146 if(temp==1)
147 r=mid-1;
148 }
149 return ans;
150 }
151 int main()
152 {
153 input();
154 fa[1]=1;
155 tarjan(1,0);
156 printf("%d\n",find_ans(0,maxx));
157 return 0;
158 }
7.