给你一个图,让你构建一个最小路径树,然后问在构建出来的最小路径树中,恰好过k个点的简单路中长度最长的链的长度是多少,同样长度的链有多少条?
题目要求构建最小路径树的方法是,每个点都选取从点1到该点的最短路径中字典序最小的那条路放到树中。
数据范围:点的个数为3*10^5
首先构造最小路径树,我们先进行一遍dijkstra,求出根到每个点的最短路径。然后进行一遍dfs,构建出最小路径树。
然后使用分治算法,每次寻找过重心的恰好过k个点的简单路即可。寻找过重心的恰好过k个点的简单路使用dp,记录dp[i]为只考虑当前这些孩子的情况下,过i个点的一个端点是重心的简单路的最大长度及其次数。
#include <cstdio>
#include <vector>
#include <cstring>
#include <queue>
#include <iostream>
#include <algorithm>
using namespace std;
struct Node {
int fe,v,num;
bool visited;
};
struct Edge {
int t,ne,v;
bool legal;
};
Node a[30001];
Edge b[120000];
int n,m,p,k;
void init() {
for (int i=1;i<=n;i++) {
a[i].fe=-1;
a[i].v=-1;
a[i].visited=false;
}
p=0;
}
void putedge(int x,int y,int z) {
b[p].t=y;
b[p].ne=a[x].fe;
b[p].v=z;
b[p].legal=false;
a[x].fe=p++;
}
struct HeapNode {
int i,v;
HeapNode() {}
HeapNode(int ii,int vv) {
i=ii;v=vv;
}
friend bool operator < (const HeapNode &a,const HeapNode &b) {
if (a.v!=b.v) return a.v>b.v;
return a.i>b.i;
}
};
priority_queue <HeapNode> prique;
void getShortestPath(int s) {
while (!prique.empty()) prique.pop();
a[s].v=0;
prique.push(HeapNode(s,0));
while (!prique.empty()) {
int i=prique.top().i;
int v=prique.top().v;
prique.pop();
if (a[i].v==v) {
for (int j=a[i].fe;j!=-1;j=b[j].ne) {
if (a[b[j].t].v==-1||a[b[j].t].v>v+b[j].v) {
a[b[j].t].v=v+b[j].v;
prique.push(HeapNode(b[j].t,a[b[j].t].v));
}
}
}
}
}
void print() {
for (int i=1;i<=n;i++)
printf("%d %d\n",i,a[i].v);
}
bool cmp(int x,int y) {
return b[x].t<b[y].t;
}
void buildShortestPathTree(int i) {
a[i].visited=true;
vector<int>tmp;
for (int j=a[i].fe;j!=-1;j=b[j].ne) {
tmp.push_back(j);
}
sort(tmp.begin(),tmp.end(),cmp);
for (int j=0;j<tmp.size();j++) {
if (!a[b[tmp[j]].t].visited&&a[b[tmp[j]].t].v==a[i].v+b[tmp[j]].v) {
b[tmp[j]].legal=true;
b[tmp[j]^1].legal=true;
buildShortestPathTree(b[tmp[j]].t);
}
}
}
struct AnsNode {
int len,num;
void clear() {
len=num=-1;
}
void update(int l,int n=1) {
if (l>len) {
len=l;
num=n;
} else if (l==len) {
num+=n;
}
}
};
AnsNode ans;
int center,centerV,nn,dpn;
AnsNode dp[30000],curdp[30000];
void getNum(int i) {
a[i].visited=true;
a[i].num=1;
for (int j=a[i].fe;j!=-1;j=b[j].ne) {
if (b[j].legal&&!a[b[j].t].visited) {
getNum(b[j].t);
a[i].num+=a[b[j].t].num;
}
}
a[i].visited=false;
}
void getCenter(int i) {
int tmp=nn-a[i].num;
a[i].visited=true;
for (int j=a[i].fe;j!=-1;j=b[j].ne) {
if (b[j].legal&&!a[b[j].t].visited) {
getCenter(b[j].t);
tmp=max(tmp,a[b[j].t].num);
}
}
if (tmp<centerV) {
centerV=tmp;
center=i;
}
a[i].visited=false;
}
void getdp(int i,int v,int l) {
if (l>=k) return;
curdp[l].update(v);
if (dp[k-l-1].num!=-1) ans.update(v+dp[k-l-1].len,dp[k-l-1].num);
a[i].visited=true;
for (int j=a[i].fe;j!=-1;j=b[j].ne) {
if (b[j].legal&&!a[b[j].t].visited) {
getdp(b[j].t,v+b[j].v,l+1);
}
}
a[i].visited=false;
}
void divideAndConquer(int i) {
getNum(i);
nn=centerV=a[i].num;
if (nn<k) return;
getCenter(i);
i=center;
a[i].visited=true;
for (int t=min(k,nn);t>=0;t--) dp[t].clear();
dp[0].update(0);
for (int j=a[i].fe;j!=-1;j=b[j].ne) {
if (b[j].legal&&!a[b[j].t].visited) {
for (int t=min(k,a[b[j].t].num);t>=0;t--) curdp[t].clear();
getdp(b[j].t,b[j].v,1);
for (int t=min(k,a[b[j].t].num);t>=0;t--) dp[t].update(curdp[t].len,curdp[t].num);
}
}
for (int j=a[i].fe;j!=-1;j=b[j].ne) {
if (b[j].legal&&!a[b[j].t].visited) {
divideAndConquer(b[j].t);
}
}
}
int main() {
int i,j,t,x,y,z;
scanf("%d",&t);
while (t--) {
scanf("%d%d%d",&n,&m,&k);
init();
for (i=0;i<m;i++) {
scanf("%d%d%d",&x,&y,&z);
putedge(x,y,z);
putedge(y,x,z);
}
getShortestPath(1);
buildShortestPathTree(1);
for (i=1;i<=n;i++) a[i].visited=false;
ans.clear();
divideAndConquer(1);
printf("%d %d\n",ans.len,ans.num);
}
return 0;
}