【Usaco Oct08 Gold】奶牛串门(Pasture Walking)
Time Limit:30000MS Memory Limit:65536K
Total Submit:75 Accepted:50
Case Time Limit:3000MS
Description
有N (2 <= N <= 1,000)头奶牛,分别编号为1到N。还有N颗牧草分别编号为1到N。简单起见,第i头奶牛都盯着第i颗牧草。
有几对牧草分别用一些小路连接了起来,总共有N-1条双向的小路,小路i连接了Ai及Bi颗牧草(1 <= Ai <= N; 1 <= Bi <= N),小路的长度为Li (1 <= Li <= 10,000)保证任意两颗牧草总能通过小路走到,也就是说这些小路构成了一棵树。
奶牛是群居性的动物,很喜欢串门。奶牛们会问你Q次问题(1 <= Q <= 1,000),每次询问的内容很简单,就是从p1颗牧草到p2颗牧草最短的距离是多少。(1 <= p1 <= N; 1 <=p2 <= N)
Input
第一行,两个用空格分隔的整数:N和Q
第二行到第N行,第i+1行包含三个用空格分隔的整数Ai,Bi,Li
第N+1行到第N+Q行,每行包含两个用空格分隔的整数p1,p2
Output
共Q行,每行包含每次询问的最短距离的值。
Sample Input
4 2
2 1 2
4 3 2
1 4 3
1 2
3 2
Sample Output
2
7
Hint
样例解释:
询问1:1->2 总代价为2
询问2:3->4->1->2 总代价为7
Source
Usaco October 2008 Gold
这本来算是一道裸题的,但是由于我对倍增算法不熟悉,犯了以下一个傻逼的错误,导致了惨痛的4连WA:
len[x][i]记录的是x节点向上走(1<<i)步的权值,而我犯错误的是,我把向上走(1<<i)写成了len[x][fa[x][i]],只有呵呵了。
然后读了416162623 的代码我才知道自己错在哪,呃,还有就是相邻的点的权值不一定要用n2的复杂度来记录,416162623是存在边里边的,空间小速度快,我这个就相形见绌了。
416162623的代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
using namespace std;
int n,q;
int cnt=0;
int f[1005][50];
int M[1005][50];
int depth[10005];
bool mark[10005];
int last[10005],next[10005],end[10005],len[10005];
void dfs(int x)
{
mark[x]=true;
int a,b,c,d,e;
depth[x]=depth[f[x][0]]+1;
a=ceil(log(depth[x])/log(2));
for(b=1;b<=a;b++)
{
f[x][b]=f[f[x][b-1]][b-1];
M[x][b]=M[x][b-1]+M[f[x][b-1]][b-1];
}
b=last[x];
while(b!=0)
{
if(mark[end[b]]==false)
{
f[end[b]][0]=x;
M[end[b]][0]=len[b];
dfs(end[b]);
}
b=next[b];
}
}
int LCA(int x,int y)
{
int a,b,c,d,e;
int ans=0;
if(depth[x]>depth[y]){a=x;x=y;y=a;}
e=ceil(log(n)/log(2));
int k=depth[y]-depth[x];
for(a=0;a<=e;a++)
if(k&(1<<a))
{
ans+=M[y][a];
y=f[y][a];
}
if(x==y)return ans;
e=ceil(log(depth[x])/log(2));
for(a=e;a>=0;a--)
if(f[x][a]!=f[y][a])
{
ans+=M[x][a];
x=f[x][a];
ans+=M[y][a];
y=f[y][a];
}
ans+=M[x][0];
ans+=M[y][0];
return ans;
}
int main()
{
int a,b,c,d,e;
scanf("%d%d",&n,&q);
for(a=1;a<=n-1;a++)
{
scanf("%d%d%d",&b,&c,&d);
cnt++;
end[cnt]=c;
len[cnt]=d;
next[cnt]=last[b];
last[b]=cnt;
cnt++;
end[cnt]=b;
len[cnt]=d;
next[cnt]=last[c];
last[c]=cnt;
}
dfs(1);
for(a=1;a<=q;a++)
{
scanf("%d%d",&b,&c);
printf("%d\n",LCA(b,c));
}
return 0;
}
我的大空间复杂度代码
#include<cstdio>
#include<vector>
#include<cmath>
using namespace std;
#define maxn 1009
int dep[maxn], fa[maxn][13], len[maxn][13], zlen[maxn][maxn];
bool wasfa[maxn];
vector<int>edge[maxn];
int BigS;
void dfs(int cur)
{
int k, i, son;
dep[cur]=dep[fa[cur][0]]+1;
k=ceil(log(dep[cur])/log(2));
for(i=1; i<=k; i++)
{
fa[cur][i]=fa[fa[cur][i-1]][i-1];
len[cur][i]=len[cur][i-1]+len[fa[cur][i-1]][i-1];
//原来错写成len[cur][i]=len[cur][fa[cur][i-1]]+...概念理解错误
}
wasfa[cur]=1;
vector<int>::iterator it;
for(it=edge[cur].begin(); it!=edge[cur].end(); it++)
{
son=(*it);
if(wasfa[son]==0)
{
fa[son][0]=cur;
len[son][0]=zlen[son][cur];
dfs(son);
}
}
}
int lca(int x, int y)
{
int i, k, s, ans=0;
if(dep[x]<dep[y]) swap(x, y);
k=dep[x]-dep[y];
for(i=0; i<=BigS; i++)
{
if(k&(1<<i))
{
ans+=len[x][i];
x=fa[x][i];
}
}
if(x==y) return ans;
s=ceil(log(dep[x])/log(2));
for(i=s; i>=0; i--)
{
if(fa[x][i]!=fa[y][i])
{
ans+=len[x][i];
x=fa[x][i];
ans+=len[y][i];
y=fa[y][i];
}
}
return ans+len[x][0]+len[y][0];
}
int main()
{
int n, q, i, a, b, l, p1, p2;
scanf("%d%d", &n, &q);
BigS=ceil(log(n)/log(2));
for(i=1; i<n; i++)
{
scanf("%d%d%d", &a, &b, &l);
zlen[a][b]=l;
zlen[b][a]=l;
edge[a].push_back(b);
edge[b].push_back(a);
}
dfs(1);
for(i=1; i<=q; i++)
{
scanf("%d%d", &p1, &p2);
printf("%d\n", lca(p1, p2));
}
return 0;
}
本文解析USACO Gold级别的“奶牛串门”问题,介绍如何使用倍增算法解决路径查找问题,避免常见错误并提供高效实现代码。
1342

被折叠的 条评论
为什么被折叠?



