【FJOI2014】最短路径树问题
【题目大意】给一个包含n个点,m条边的无向连通图。从顶点1出发,往其余所有点分别走一次并返回。
往某一个点走时,选择总长度最短的路径走。若有多条长度最短的路径,则选择经过的顶点序列字典序最小的那条路径(如路径A为1,32,11,路径B为1,3,2,11,路径B字典序较小。注意是序列的字典序的最小,而非路径中节点编号相连的字符串字典序最小)。到达该点后按原路返回,然后往其他点走,直到所有点都走过。
可以知道,经过的边会构成一棵最短路径树。请问,在这棵最短路径树上,最长的包含K个点的简单路径长度为多长?以及长度为该最长长度的不同简单路径有多少条?
这里的简单路径是指:对于一个点最多只经过一次的路径。不同路径是指路径两端端点至少有一个不同,点A到点B的路径和点B到点A视为同一条路径。
往某一个点走时,选择总长度最短的路径走。若有多条长度最短的路径,则选择经过的顶点序列字典序最小的那条路径(如路径A为1,32,11,路径B为1,3,2,11,路径B字典序较小。注意是序列的字典序的最小,而非路径中节点编号相连的字符串字典序最小)。到达该点后按原路返回,然后往其他点走,直到所有点都走过。
可以知道,经过的边会构成一棵最短路径树。请问,在这棵最短路径树上,最长的包含K个点的简单路径长度为多长?以及长度为该最长长度的不同简单路径有多少条?
这里的简单路径是指:对于一个点最多只经过一次的路径。不同路径是指路径两端端点至少有一个不同,点A到点B的路径和点B到点A视为同一条路径。
【题解】今天太晚了以后再写
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define fo(i,a,b) for (int i = a;i <= b;i ++)
using namespace std;
const int maxn = 30005;
int N,M,K,ans,sum,root,size,cnt;
int s[maxn],f[maxn];
struct graph
{
int a[maxn],b[maxn*4],c[maxn*4],d[maxn*4],tot;
bool flag[maxn];
void Insert(int,int,int);
void SPFA();
void Findroot(int,int);
void dfs(int,int,int,int,int);
void collect(int,int,int,int,int);
void Work(int,int);
}OLD,NEW;
struct T
{
int l,n,t;
inline int ql(int TIME)
{
if (TIME != t)
{
t = TIME;
l = -1000000;
n = 0;
return 0;
}
return l;
}
};
void graph::Insert(int x,int y,int z)
{
tot ++;
b[tot] = y;
d[tot] = z;
c[tot] = a[x];
a[x] = tot;
}
void graph::SPFA()
{
static int q[maxn*5];
static bool bz[maxn];
static int last[maxn],weight[maxn];
static vector<int> path[maxn];
static int dist[maxn];
fo(i,2,N) path[i].push_back(50000);
memset(dist,127,(N+1)*4);
int l = 0, r = 1;
q[1] = 1; dist[1] = 0;
bz[1] = 1;
path[1].push_back(1);
while (l < r)
{
int x = q[++l];
for (int i = a[x];i;i = c[i])
if (dist[x] + d[i] <= dist[b[i]])
{
static vector<int> temp;
temp = path[x];
temp.push_back(b[i]);
if (dist[x] + d[i] < dist[b[i]] || temp < path[b[i]])
{
path[b[i]] = temp;
dist[b[i]] = dist[x] + d[i];
last[b[i]] = x, weight[b[i]] = d[i];
if (!bz[b[i]])
{
bz[b[i]] = 1;
q[++r] = b[i];
}
}
}
bz[x] = 0;
}
fo(i,2,N)
{
NEW.Insert(last[i],i,weight[i]);
NEW.Insert(i,last[i],weight[i]);
}
}
void graph::Findroot(int x,int last)
{
s[x] = 1;
f[x] = 0;
for (int i = a[x];i;i = c[i])
if (b[i] != last && !flag[b[i]])
{
Findroot(b[i],x);
s[x] += s[b[i]];
f[x] = max(f[x],s[b[i]]);
}
f[x] = max(f[x],size-s[x]);
if (f[x] < f[root]) root = x;
}
T tank[maxn];
inline void update(int x,int _l,int TIME)
{
if (tank[x].t == TIME)
{
if (_l > tank[x].l)
{
tank[x].l = _l;
tank[x].n = 1;
} else
if (_l == tank[x].l)
tank[x].n ++;
} else
{
tank[x].l = _l;
tank[x].n = 1;
}
tank[x].t = TIME;
}
inline void upd_ans(int l,int cnt)
{
if (l > ans)
{
ans = l;
sum = cnt;
} else
if (l == ans) sum += cnt;
}
void graph::collect(int x,int dep,int len,int last,int TIME)
{
update(dep,len,TIME);
for (int i = a[x];i;i = c[i])
if (b[i] != last && !flag[b[i]])
collect(b[i],dep+1,len+d[i],x,TIME);
}
void graph::dfs(int x,int dep,int len,int last,int TIME)
{
if (K-dep > 0)
upd_ans(tank[K-dep-1].ql(TIME)+len,tank[K-dep-1].n);
for (int i = a[x];i;i = c[i])
if (b[i] != last && !flag[b[i]])
dfs(b[i],dep+1,len+d[i],x,TIME);
}
void graph::Work(int x,int t)
{
tank[0].t = t;
tank[0].l = 0;
tank[0].n = 1;
for (int i = a[x];i;i = c[i])
if (!flag[b[i]])
{
dfs(b[i],1,d[i],x,t);
collect(b[i],1,d[i],x,t);
}
flag[x] = 1;
for (int i = a[x];i;i = c[i])
if (!flag[b[i]])
{
f[0] = size = s[b[i]];
Findroot(b[i],root = 0);
Work(root,++cnt);
}
}
int main()
{
freopen("short.in","r",stdin);
freopen("short.out","w",stdout);
scanf("%d%d%d",&N,&M,&K);
fo(i,1,M)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
OLD.Insert(x,y,z);
OLD.Insert(y,x,z);
}
OLD.SPFA();
f[0] = size = N;
NEW.Findroot(1,root = 0);
NEW.Work(root,cnt = 1);
printf("%d %d\n",ans,sum);
return 0;
}