题目在这里
有生之年参加的第一次省选,当时太年轻太弱又碰到3道丧病题果断爆零了。(虽然现在还是很弱)考场上以为这是一道树形dp,因为树上的题目除了dfs和dp外啥都不知道。。。。。
这题是点分治大裸题(我也只会写大裸题),然而我建树写了两节课。。。大致就是先从1号点跑一遍spfa或heap-dijkstra,显然满足dis[x]+w[x][y]==dis[y]的边在最短路径上。由于要求路径字典序最小,就优先走编号小的节点,这样一个点第一次被dfs到就一定是1到它的路径字典序最小了。
建完树之后直接点分治。记一下每棵子树的到重心刚好经过x个节点的路径的最大值_max[x],在记录一下这个最大值出现多少次,然后无脑码个150行注意一下就A了。
//最短路树+点分治 记录方案数
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <ctime>
using namespace std;
const int MAXN=30005,MAXM=60005,INF=1000000005;
struct Edge
{
int pre,from,to,cost,pos; bool flag;
}E[MAXM*2],tmp[MAXM*2];
int n,m,K,tot,fst[MAXN],dis[MAXN],depth[MAXN],size[MAXN],maxsize[MAXN],w[MAXN],_max[MAXN],nowmax[MAXN],st,ed,
nowroot,cnt,amo[MAXN],nowamo[MAXN],son[MAXN],ans,sum;
bool vis[MAXN];
struct Node
{
int x,val;
Node () {} Node(int _x,int _val) { x=_x,val=_val; }
};
priority_queue<Node> H;
bool operator < (Node a,Node b) { return a.val>b.val; }
bool cmp1(const Edge &a,const Edge &b) { return (a.from<b.from) || (a.from==b.from && a.to<b.to); }
bool cmp2(const Edge &a,const Edge &b) { return a.pos<b.pos; }
int Get()
{
char ch; int v=0; bool f=false;
while (!isdigit(ch=getchar())) if (ch=='-') f=true; v=ch-48;
while (isdigit(ch=getchar())) v=v*10+ch-48;
if (f) return -v;else return v;
}
void add(int x,int y,int c,bool f)
{
E[++tot].pre=fst[x],fst[x]=tot,E[tot].from=x,E[tot].to=y,E[tot].cost=c,E[tot].flag=false;
if (f) E[++tot].pre=fst[y],fst[y]=tot,E[tot].from=y,E[tot].to=x,E[tot].cost=c,E[tot].flag=false;
}
void Dijkstra()
{
for (int i=1;i<=n;i++) dis[i]=INF,vis[i]=false; dis[1]=0,H.push(Node(1,dis[1])),vis[1]=true;
while (!H.empty())
{
Node tmp=H.top(); H.pop();
int x=tmp.x; if (tmp.val!=dis[x]) continue; vis[x]=true;
for (int i=fst[x];i;i=E[i].pre)
{
int y=E[i].to;
if (!vis[y] && dis[x]+E[i].cost<dis[y]) dis[y]=dis[x]+E[i].cost,H.push(Node(y,dis[y]));
}
}
}
void dfs_init(int x) //建树
{
vis[x]=true;
for (int i=fst[x];i;i=E[i].pre)
{
int y=E[i].to;
if (!vis[y] && dis[x]+E[i].cost==dis[y]) depth[y]=depth[x]+1,E[i].flag=true,dfs_init(y);
}
}
void Build_Tree()
{
for (int i=1;i<=tot;i++) tmp[i].from=E[i].from,tmp[i].to=E[i].to,tmp[i].cost=E[i].cost,tmp[i].pos=i;
sort(tmp+1,tmp+1+tot,cmp1); for (int i=1;i<=n;i++) vis[i]=false,fst[i]=0; int ttot=tot; tot=0;
for (int i=ttot;i>=1;i--) add(tmp[i].from,tmp[i].to,tmp[i].cost,0);
depth[1]=1; dfs_init(1);
for (int i=1;i<=tot;i++) tmp[i].from=E[i].from,tmp[i].to=E[i].to,tmp[i].cost=E[i].cost,tmp[i].flag=E[i].flag;
for (int i=1;i<=n;i++) fst[i]=0,vis[i]=false; ttot=tot; tot=0;
for (int i=1;i<=ttot;i++) if (tmp[i].flag) add(tmp[i].from,tmp[i].to,tmp[i].cost,1); maxsize[0]=MAXN;
}
void dfs_size(int x,int fa) //算出孩子数
{
maxsize[x]=0,size[x]=1,cnt++;
for (int i=fst[x];i;i=E[i].pre)
{
int y=E[i].to;
if (y!=fa && !vis[y]) dfs_size(y,x),size[x]+=size[y],maxsize[x]=max(maxsize[x],size[y]);
}
}
void dfs_root(int x,int fa) //找重心
{
maxsize[x]=max(maxsize[x],cnt-size[x]);
if (maxsize[x]<maxsize[nowroot]) nowroot=x;
for (int i=fst[x];i;i=E[i].pre)
{
int y=E[i].to;
if (y!=fa && !vis[y]) dfs_root(y,x);
}
}
void modify(int &a,int b,int &c,int v) { if (b>a) a=b,c=v;else if (a==b) c+=v; }
void dfs_work(int x,int fa) //暴力统计
{
if (depth[x]>K) return;
son[++tot]=x; int tmp=_max[K-depth[x]]+w[x];
modify(nowmax[depth[x]],w[x],nowamo[depth[x]],1); modify(ans,tmp,sum,amo[K-depth[x]]);
for (int i=fst[x];i;i=E[i].pre)
{
int y=E[i].to;
if (y!=fa && !vis[y]) w[y]=w[x]+E[i].cost,depth[y]=depth[x]+1,dfs_work(y,x);
}
}
void work(int root) //计算路径经过x的答案
{
vis[root]=true; w[root]=0,depth[root]=0; tot=0;
for (int i=fst[root];i;i=E[i].pre)
{
int y=E[i].to;
if (!vis[y])
{
st=tot+1,depth[y]=1,w[y]=E[i].cost,dfs_work(y,-1),ed=tot;
for (int j=st;j<=ed;j++)
modify(_max[depth[son[j]]],nowmax[depth[son[j]]],amo[depth[son[j]]],nowamo[depth[son[j]]]),
nowmax[depth[son[j]]]=0,nowamo[depth[son[j]]]=0;
}
}
for (int i=1;i<=tot;i++)
{
if (depth[son[i]]==K) modify(ans,w[son[i]],sum,1);
nowmax[depth[son[i]]]=_max[depth[son[i]]]=amo[depth[son[i]]]=nowamo[depth[son[i]]]=0;
}
}
void dfs(int x,int fa) //最外层dfs
{
cnt=nowroot=0; int root=0; dfs_size(x,fa);
dfs_root(x,fa); root=nowroot;
work(root);
for (int i=fst[root];i;i=E[i].pre)
{
int y=E[i].to;
if (!vis[y]) dfs(y,root);
}
}
int main()
{
freopen("mindistree.in","r",stdin);
freopen("mindistree.out","w",stdout);
n=Get(),m=Get(),K=Get(); K--; int u,v,d; tot=0;
for (int i=1;i<=m;i++) u=Get(),v=Get(),d=Get(),add(u,v,d,1);
Dijkstra();
Build_Tree();
ans=sum=0;
dfs(1,-1);
printf("%d %d\n",ans,sum);
return 0;
}