1.cojs 186. [USACO Oct08] 牧场旅行
★★ 输入文件:pwalk.in
输出文件:pwalk.out
简单对比
时间限制:1 s 内存限制:128 MB
n个被自然地编号为1..n奶牛(1<=n<=1000)正在同样被方便的编号为1..n的n个牧场中吃草。更加自然而方便的是,第i个奶牛就在第i个牧场中吃草。
其中的一些对牧场被总共的n-1条双向通道的一条连接。奶牛可以通过通道。第i条通道连接的两个牧场是A_i和B_i(1<=A_i<=N;1<=B_i<=N)其长度是L_i(1<=L_i<=10000)。
通道只会连接两个不同的牧场,所以这些通道使得整个牧场构成了一棵树。
奶牛们是好交际的希望能够经常的访问别的奶牛。急切地,它们希望你能通过告诉它们Q(1<=Q<=1000)对牧场的路径来帮助他们安排旅行。(这里将有Q个询问,p1,p2(1<=p1<=n;1<=p1<=n))
分数:200
问题名称:pwalk
输入格式:
- 第1行:两个用空格隔开的整数:n和Q
- 第2..n行:第i+1行包含三个用空格隔开的整数:A_i,B_i和L_i
- 第n+1..N+Q行:每行包含两个用空格隔开的整数,代表两个不同的牧场,p1和p2
输入样例(file pwalk.in):
4 2 2 1 2 4 3 2 1 4 3 1 2 3 2
输出格式:
- 第1..Q行:行i包含第i个询问的答案。
输出样例:
2 7
输出说明:
询问1:牧场1和牧场2的路径长度为2。 询问2:3->4->1->2;总长为7。
#include<iostream> using namespace std; #include<cstdio> #include<cstring> #define Er 20 #define N 1011 int deepp[N],head[N]; int father[N][Er]; struct Edge{ int v,last,w; }edge[N<<1]; int t=0,n,Q,ai,bi,li; int p1,p2; typedef long long ll; ll dis[N]={0}; void add_edge(int u,int v,int w) { ++t; edge[t].v=v; edge[t].w=w; edge[t].last=head[u]; head[u]=t; } void input() { scanf("%d%d",&n,&Q); for(int i=1;i<=n-1;++i) { scanf("%d%d%d",&ai,&bi,&li); add_edge(ai,bi,li); add_edge(bi,ai,li); } } void dfs(int k) { for(int l=head[k];l;l=edge[l].last) { if(!deepp[edge[l].v]) { deepp[edge[l].v]=deepp[k]+1; dis[edge[l].v]=dis[k]+edge[l].w; father[edge[l].v][0]=k; dfs(edge[l].v); } } } void pre_chuli() { for(int j=1;(1<<j)<=n;++j) for(int i=1;i<=n;++i) if(father[i][j-1]!=-1) father[i][j]=father[father[i][j-1]][j-1]; } int lca(int a,int b) { if(deepp[a]<deepp[b]) swap(a,b); int i; for(i=0;(1<<i)<=deepp[a];++i); i--; for(int j=i;j>=0;--j) if(deepp[a]-deepp[b]>=(1<<j)) a=father[a][j]; if(a==b) return a; for(int j=i;j>=0;--j) if(father[a][j]!=-1&&father[a][j]!=father[b][j]) { a=father[a][j]; b=father[b][j]; } return father[a][0]; } int main() { freopen("pwalk.in","r",stdin); freopen("pwalk.out","w",stdout); input(); memset(father,-1,sizeof(father)); dis[1]=0; deepp[1]=1; dfs(1); pre_chuli(); for(int i=1;i<=Q;++i) { scanf("%d%d",&p1,&p2); int ance=lca(p1,p2); cout<<dis[p1]+dis[p2]-2*dis[ance]<<endl; } fclose(stdin);fclose(stdout); return 0; }
2.cojs 2075. [ZLXOI2015][异次元圣战III]ZLX的陨落
★★☆ 输入文件:ThefallingofZLX.in
输出文件:ThefallingofZLX.out
简单对比
时间限制:1 s 内存限制:256 MB
【题目描述】
正当革命如火如荼,情侣教会日薄西山之时,SOX和FFF的分歧却越来越大,SOX认为情侣教会不合适的、无限制的秀恩爱和阶级歧视值得反对,而FFF认为一切情侣都该从这个世界上消失。
SOX先发制人,率先接管了全国政权并突袭了FFF,暗杀了FFF的首领,FFF的红色革命事业遭到了空前的打击,一些FFF团员积极抵抗,另一些FFF团员隐居避世,而一些FFF团员则走向极端,参加了一个邪恶组织:诅咒教会
你虽然对SOX下山摘桃子的行为不满,但你对邪教更不满。在对诅咒教会的长期调查中,你发现该组织的操纵者是亡灵巫师ZLX!现在,维护正义的使命降到了你身上!你和其他的一些远征军将士前往ZLX的城堡,却掉入了ZLX的魔法陷阱--扭曲虚空,扭曲虚空由n个魔法结界空间组成,m条虚空隧道连接着它们,你和其他的远征军将士(恰好有n个)分散在魔法结界空间里,只有会合在一起,你们才能冲破封锁(扭曲虚空是一颗树)。现在,你向平行世界的你提出了疑问,如果给出两个人会合,总共至少需要多少魔法能量?
已知虚空隧道的长度与消耗的魔法能量在数值上相等。
ZLX的末日已经到临,等到你冲出虚空隧道,亲手结果了ZLX吧!
【输入格式】
第一行一个正整数N,代表魔法结界空间的个数,一个正整数M,代表虚空隧道的个数
接下来M行,每行三个数u,v,w,代表虚空隧道连接的两个点和虚空隧道的长度
接下来一个正整数Q,代表查询个数
接下来Q行,每行两个数u,v代表询问从u到v需要消耗的魔法能量
【输出格式】
Q行,每行一个正整数
【样例输入】
6 5 1 2 7 1 3 3 2 4 5 3 5 7 4 6 6 5 3 4 6 3 5 1 4 3 4 2
【样例输出】
15 21 10 15 5
【提示】
对于20%的数据,n<=300,q<=300
对于40%的数据,n<=2000,q<=2000
对于100%的数据,n<=100000,q<=100000,w<=32767,m=n-1
1 #define N 100100 2 #include<iostream> 3 using namespace std; 4 #include<cstdio> 5 #include<cstring> 6 #define Er 20 7 typedef long long ll; 8 ll dis[N]={0}; 9 int deepp[N],father[N][Er],t,n,m,u1,v1,w1,Q; 10 struct Edge{ 11 int v,last,w; 12 }edge[N<<1]; 13 int head[N]; 14 void add_edge(int u,int v,int w) 15 { 16 ++t; 17 edge[t].v=v; 18 edge[t].w=w; 19 edge[t].last=head[u]; 20 head[u]=t; 21 } 22 void input() 23 { 24 scanf("%d%d",&n,&m); 25 for(int i=1;i<=m;++i) 26 { 27 scanf("%d%d%d",&u1,&v1,&w1); 28 add_edge(u1,v1,w1); 29 add_edge(v1,u1,w1); 30 } 31 } 32 void dfs(int k) 33 { 34 for(int l=head[k];l;l=edge[l].last) 35 { 36 if(!deepp[edge[l].v]) 37 { 38 deepp[edge[l].v]=deepp[k]+1; 39 father[edge[l].v][0]=k; 40 dis[edge[l].v]=dis[k]+edge[l].w; 41 dfs(edge[l].v); 42 } 43 } 44 } 45 void pre_chuli() 46 { 47 for(int j=1;(1<<j)<=n;++j) 48 for(int i=1;i<=n;++i) 49 if(father[i][j-1]!=-1) 50 father[i][j]=father[father[i][j-1]][j-1]; 51 } 52 int lca(int a,int b) 53 { 54 if(deepp[a]<deepp[b]) swap(a,b); 55 int i; 56 for(i=0;(1<<i)<=deepp[a];++i); 57 i--; 58 for(int j=i;j>=0;--j) 59 if(deepp[a]-deepp[b]>=(1<<j)) 60 a=father[a][j]; 61 if(a==b) return a; 62 for(int j=i;j>=0;--j) 63 if(father[a][j]!=-1&&father[a][j]!=father[b][j]) 64 { 65 a=father[a][j]; 66 b=father[b][j]; 67 } 68 return father[a][0]; 69 } 70 int main() 71 { 72 freopen("ThefallingofZLX.in","r",stdin); 73 freopen("ThefallingofZLX.out","w",stdout); 74 input(); 75 memset(father,-1,sizeof(father)); 76 dis[1]=0; 77 deepp[1]=1; 78 dfs(1); 79 pre_chuli(); 80 scanf("%d",&Q); 81 for(int i=1;i<=Q;++i) 82 { 83 scanf("%d%d",&u1,&v1); 84 int ance=lca(u1,v1); 85 cout<<dis[u1]+dis[v1]-2*dis[ance]<<endl; 86 } 87 fclose(stdin);fclose(stdout); 88 return 0; 89 }
3.codevs 3287 货车运输
2013年NOIP全国联赛提高组
A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。
接下来 m 行每行 3 个整数 x、y、z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意:x 不等于 y,两座城市之间可能有多条道路。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意:x 不等于 y。
输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。
4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3
3
-1
3
对于 30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q < 1,000;
对于 60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q < 1,000;
对于 100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q < 30,000,0 ≤ z ≤ 100,000。
/*--------------解析---------------------*/ 拿到这道题,我们求的是多组两点间通路上最大的最小边权,最大生成树的两点间最小边权就满足这个性质,所以就用到到最大生成树 很容易得到60分算法 60分算法:(最大生成树暴力) 先找一个最大生成树,然后把生成树单独放在一个类似生成树存储方式的三个数组里,初始化并查集,然后从最大边开始做kruskal,每次连边时,都对问题集合进行扫描,当且仅当这两个边联通且没有更新过当前问题的答案,就用当前边权更新。原因很简单,这是贪心,首先边权时降序的,然后这个边使两点第一次联通,那么这个边一定是通路上最小的边。这就是60的算法。但是注意,题目并没有说是不是一棵树,所以它可以是森林,但是由于六十分算法原理对于森林这个性质并不依赖,所以,这个60算法不用考虑森林 那么我们想一下,这道题时间的瓶颈在哪,首先kruskal是o(m)的,这个不会爆时间,那么剩下就是从最大生成树上回答答案所用时间过长。 我们想一下,如果是在logn时间内回答一个问题,而且是在树上做的一个算法,尤其是这个问题具有区间求和的性质,因为想一想就知道这是显然的 f[i→j]:=min(f[i→k],f[k→j]), 所以,我们想到用树上倍增算法解决。 100分算法(最大生成树+树上倍增): 首先清楚树上倍增原理:f[i,j]:=f[f[I,j-1],j-1] f[I,j],表示 i向上2^j的节点 同时,设我们要求的答案g[i,j],即从i向上走到达2^j层的节点所通过路径上的最小值,转移方程 g[I,j]:=min(g[f[I,j-1],j-1],g[I,j-1]) 分析一下就是显然的,g[f[I,j-1],j-1]表示的是i向上2^(j-1)的节点到2^j节点间的最小值,g[I,j-1] 是从i向上走到达2^(j-1)层的节点所通过路径上的最小值,两个区间合起来就是当前的区间长度,所以这个方程成立。对于初值,我们可以bfs求深度并记录,与此同时,f[I,0]是父节点,g[I,0]是他向上走一层的边的长度。然后按照倍增算法模板生成f g数组 求解时,用倍增算法模板即可,只不过有几个细节 1、 别忘了给ans赋初值 2、 倍增算法永远是倍增到最近公共祖先的下一层的两个点,所以最后要比较一下ans与那两个点的向上走一步的值 3、 记得这是个森林,要用一个数组记一下那个是父节点,我用的是并查集加bool数组,有个细节是不要直接把fa[i]当I的所在树的根,要get一下,因为并查集的压缩路径是在每次get时才更新,当他fa变了,而他未被查询过,就会导致fa过时了,要重新get一下 4、 在对齐两个所求节点的层数时,别忘了更新答案 大概就是这些,还有相关代码具体实现就不赘述了。 哦,对了,提交不要忘了删除调试用的输出
1 /* 2 60分代码:暴力建q棵最大生成树 3 */ 4 #define N 10008 5 #define M 50007 6 #include<iostream> 7 using namespace std; 8 #include<cstdio> 9 #include<cstring> 10 #include<algorithm> 11 int n,m,q,fa[N],head[N],t=0; 12 struct Edge{ 13 int u,v,w,last; 14 bool operator <(Edge P) 15 const{return w>P.w;} 16 }edge[M<<1]; 17 int read() 18 { 19 int ret=0,ff=1; 20 char s=getchar(); 21 while(s<'0'||s>'9') 22 { 23 if(s=='-') ff=-1; 24 s=getchar(); 25 } 26 while(s>='0'&&s<='9') 27 { 28 ret=ret*10+s-'0'; 29 s=getchar(); 30 } 31 return ret; 32 } 33 inline void add_edge(int u,int v,int w) 34 { 35 ++t; 36 edge[t].u=u; 37 edge[t].v=v; 38 edge[t].w=w; 39 edge[t].last=head[u]; 40 head[u]=t; 41 } 42 inline void inpu() 43 { 44 n=read();m=read(); 45 int x,y,z; 46 for(int i=1;i<=m;++i) 47 { 48 x=read();y=read();z=read(); 49 add_edge(x,y,z); 50 } 51 } 52 int find(int x) 53 { 54 return (fa[x]==x)?x:fa[x]=find(fa[x]); 55 } 56 int main() 57 { 58 freopen("truck.in","r",stdin); 59 freopen("truck.out","w",stdout); 60 inpu(); 61 sort(edge+1,edge+t+1); 62 q=read(); 63 int x,y; 64 for(int i=1;i<=q;++i) 65 { 66 x=read();y=read(); 67 for(int i=1;i<=n;++i) fa[i]=i; 68 bool flag=false; 69 for(int i=1;i<=t;++i) 70 { 71 int x1=find(edge[i].u); 72 int y1=find(edge[i].v); 73 if(x1!=y1) 74 { 75 fa[y1]=x1; 76 } 77 if(find(x)==find(y)) 78 { 79 printf("%d ",edge[i].w); 80 flag=true; 81 break; 82 } 83 } 84 if(!flag) printf("-1 "); 85 } 86 fclose(stdin); 87 fclose(stdout); 88 return 0; 89 }
1 #define N 10010 2 #define inf 10000800 3 #include<cstdio> 4 #define M 50002 5 #define D 18 6 #include<cstring> 7 #include<algorithm> 8 #include<cstdio> 9 using namespace std; 10 int n,m,q,fa[N],t=0; 11 struct EEdge{ 12 int u,v,w; 13 bool operator <(EEdge P) 14 const{return w>P.w;} 15 }edge1[M]; 16 struct Edge{ 17 int v,w,last; 18 }e[N]; 19 int dis[N][D+1],fath[N][D+1],dep[N],head[N]; 20 int read() 21 { 22 int ret=0,ff=1; 23 char s=getchar(); 24 while(s<'0'||s>'9') 25 { 26 if(s=='-') ff=-1; 27 s=getchar(); 28 } 29 while(s>='0'&&s<='9') 30 { 31 ret=ret*10+s-'0'; 32 s=getchar(); 33 } 34 return ret*ff; 35 } 36 void inpu() 37 { 38 n=read();m=read(); 39 for(int i=1;i<=m;++i) 40 { 41 edge1[i].u=read(); 42 edge1[i].v=read(); 43 edge1[i].w=read(); 44 } 45 } 46 int fi(int x) 47 { 48 return (fa[x]==x)?x:fa[x]=fi(fa[x]); 49 } 50 void add_edge(int u,int v,int w) 51 { 52 ++t; 53 e[t].v=v; 54 e[t].w=w; 55 e[t].last=head[u]; 56 head[u]=t; 57 } 58 void ddkruskal() 59 { 60 for(int i=1;i<=n;++i) fa[i]=i; 61 sort(edge1+1,edge1+m+1); 62 int tsum=0; 63 for(int i=1;i<=m;++i) 64 { 65 int x1=fi(edge1[i].u); 66 int x2=fi(edge1[i].v); 67 if(x1!=x2) 68 { 69 tsum++; 70 fa[x2]=x1; 71 add_edge(edge1[i].u,edge1[i].v,edge1[i].w); 72 add_edge(edge1[i].v,edge1[i].u,edge1[i].w); 73 if(tsum==n-1) break; 74 } 75 } 76 } 77 void dfs(int k) 78 { 79 for(int l=head[k];l;l=e[l].last) 80 { 81 if(!dep[e[l].v]) 82 { 83 dep[e[l].v]=dep[k]+1; 84 fath[e[l].v][0]=k; 85 dis[e[l].v][0]=e[l].w; 86 dfs(e[l].v); 87 } 88 } 89 } 90 void init() 91 { 92 for(int j=1;j<=D;++j) 93 for(int i=1;i<=n;++i) 94 { 95 fath[i][j]=fath[fath[i][j-1]][j-1]; 96 dis[i][j]=min(dis[i][j-1],dis[fath[i][j-1]][j-1]); 97 } 98 } 99 int lca(int a,int b) 100 { 101 int ans=inf; 102 if(dep[a]<dep[b]) swap(a,b); 103 for(int j=D;j>=0;--j) 104 { 105 if(dep[a]-(1<<j)>=dep[b]) 106 { 107 ans=min(ans,dis[a][j]); 108 a=fath[a][j]; 109 } 110 } 111 if(a==b) return ans; 112 for(int j=D;j>=0;--j) 113 { 114 if(fath[a][j]!=fath[b][j]) 115 { 116 ans=min(ans,min(dis[a][j],dis[b][j])); 117 a=fath[a][j]; 118 b=fath[b][j]; 119 } 120 } 121 ans=min(ans,min(dis[a][0],dis[b][0])); 122 return ans; 123 } 124 int main() 125 { 126 freopen("truck.in","r",stdin); 127 freopen("truck.out","w",stdout); 128 inpu(); 129 ddkruskal(); 130 q=read(); 131 int x,y; 132 for(int i=1;i<=n;++i) 133 { 134 if(!dep[i]) 135 { 136 dep[i]=1; 137 fath[i][0]=i; 138 dis[i][0]=inf; 139 dfs(i); 140 } 141 } 142 init(); 143 for(int i=1;i<=q;++i) 144 { 145 x=read();y=read(); 146 if(fi(x)!=fi(y)) printf("-1 "); 147 else printf("%d ",lca(x,y)); 148 } 149 fclose(stdin); 150 fclose(stdout); 151 return 0; 152 }