1 6 6 4 1 2 1 2 3 1 3 4 1 2 5 1 3 6 1 5 6 1
3 4
题意其实就是给你一个图,然后让你转换成一棵树,这棵树满足的是根节点1到其余各点的间隔都是图里的最短间隔,并且为了包管这棵树的独一性,路径也必须是最小的。转化成树的办法其实就是跑一次spfa。spfa的时辰记下所有到这个的前驱的边,然后这些边集反向的边补上就是构成所有最短路的边。然后在这些边上跑一次dfs,跑前将边遵守达到点的序号由小到大排序,重视dfs搜的下一个点的间隔必须是最短的才搜,不然的话搜出来的图就是不合错误的。
至此图的项目组转化完了,剩下的就是求一个图里包含了k个点的路径的最长间隔,以及有几许条,类似的题目还有有几许条路径的乘积=k,有几许条路径的和>k,有几许条路径的乘积是完全立方数。。。做法就是典范的树分治。
具体的做法是找出重心,对重心外的项目组递归求解,归并的时辰列举到重心的所有路径,列举的时辰可以用一个全局的map[s].k和map[s].dis分别记录不包含根节点的顶点个数是s的链的个数和该链的最长距离,时刻更新,然后用一个去列举新的项目组的路径,然后经由过程与mp存的信息对比更新答案;
网络赛的解题报告:
首先构造最短路径树。先求根节点到其他节点的最短路径,然后从根节点开始进行深度优先遍历,先遍历节点编号较小的没遍历过的儿子,这样就能处理处最短路径树。
之后找节点数为K的树链。可以用树分治进行求解,在树分治求解过程中,对于每个中心点,处理出该子树中所有节点到中心点的树链,然后枚举每条树链,比如某条树链节点为a,长度为b,则找之前遍历过的树链中节点数位K-a的长度最长的树链,将这两条树链拼起来可以得到节点数为K的树链,用其更新答案。最好一个一个分支分别处理,可以避免考虑同一个分支来的两条树链。这样枚举并且更新信息,还要存方案数。
程序:
#include"stdio.h" #include"string.h" #include"iostream" #include"map" #include"string" #include"queue" #include"stdlib.h" #include"math.h" #define M 30009 #define eps 1e-10 #define inf 1000000000 #define mod 1000000000 using namespace std; struct node { int u,w,v,next; }edge[M*4]; struct Edge { int u,v,w; }p[M*4]; struct st { int dis; __int64 k; }mp[M]; int t,k,cnt,maxi,MN,ID; __int64 ans; int head[M],use[M],dis[M],id[M],num[M],son[M],limit[M]; int cmp(const void *a,const void *b) { if((*(struct node*)a).u==(*(struct node*)b).u) return (*(struct node*)b).v-(*(struct node*)a).v; else return (*(struct node*)b).u-(*(struct node*)a).u; } void init() { t=0; memset(head,-1,sizeof(head)); } void add(int u,int v,int w) { edge[t].u=u; edge[t].v=v; edge[t].w=w; edge[t].next=head[u]; head[u]=t++; } void spfa(int s,int n) { queue<int>q; int i; for(i=1;i<=n;i++) dis[i]=inf; memset(use,0,sizeof(use)); dis[s]=0; use[s]=1; q.push(s); while(!q.empty()) { int u=q.front(); q.pop(); use[u]=0; for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].v; if(dis[v]>dis[u]+edge[i].w) { dis[v]=dis[u]+edge[i].w; p[v].u=u; p[v].w=edge[i].w; if(!use[v]) { use[v]=1; q.push(v); } } } } } void dfs_size(int u,int f) { son[u]=1; limit[u]=0; for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].v; if(f!=v&&!use[v]) { dfs_size(v,u); son[u]+=son[v]; limit[u]=max(limit[u],son[v]); } } } void dfs_root(int root,int u,int f) { if(son[root]-son[u]>limit[u]) limit[u]=son[root]-son[u]; if(MN>limit[u]) { MN=limit[u]; ID=u; } for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].v; if(f!=v&&!use[v]) { dfs_root(root,v,u); } } } void get_root(int root,int u,int f) { dfs_size(u,f); MN=inf; dfs_root(root,u,f); } void dfs_dis(int u,int f,int Rank,int w) { id[cnt++]=u; num[u]=Rank; dis[u]=w; for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].v; if(f!=v&&!use[v]) { dfs_dis(v,u,Rank+1,w+edge[i].w); } } } void cal(int u,int f) { cnt=0; for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].v; if(f==v||use[v])continue; int last=cnt; dfs_dis(v,u,2,edge[i].w); for(int j=last;j<cnt;j++) { if(num[id[j]]==k)//根节点的子链点的个数刚好是k { if(maxi<dis[id[j]])//更新最长距离,同时更新数量 { maxi=dis[id[j]]; ans=1; } else if(maxi==dis[id[j]])//相等的话ans++; { ans++; } } else if(num[id[j]]<k&&mp[k-num[id[j]]].k)//当当前子链点数小于k,则寻找k-num的子链, { if(maxi<dis[id[j]]+mp[k-num[id[j]]].dis)//更新最长距离 { maxi=dis[id[j]]+mp[k-num[id[j]]].dis; ans=mp[k-num[id[j]]].k; } else if(maxi==dis[id[j]]+mp[k-num[id[j]]].dis)//有相等的话ans加上k-num的链的个数 { ans+=mp[k-num[id[j]]].k; } } } for(int j=last;j<cnt;j++)//转化为对立链(即不包含根节点的链) { if(mp[num[id[j]]-1].dis<dis[id[j]]) { mp[num[id[j]]-1].dis=dis[id[j]]; mp[num[id[j]]-1].k=1; } else if(mp[num[id[j]]-1].dis==dis[id[j]]) { mp[num[id[j]]-1].k++; } } } for(int i=0;i<cnt;i++)//注意此时更新mp不要写成memset的形式,否则会超时 mp[i].k=mp[i].dis=0; } void dfs_ans(int u,int f) { get_root(u,u,f); cal(ID,ID); use[ID]=1; for(int i=head[ID];i!=-1;i=edge[i].next) { int v=edge[i].v; if(!use[v]) { dfs_ans(v,v); } } } void solve() { memset(mp,0,sizeof(mp)); memset(use,0,sizeof(use)); ans=maxi=0; dfs_ans(1,1); printf("%d %I64d\n",maxi,ans); } int main() { int T; cin>>T; while(T--) { int n,m,i; scanf("%d%d%d",&n,&m,&k); for(i=0;i<m*2;i+=2) { scanf("%d%d%d",&p[i].u,&p[i].v,&p[i].w); p[i^1].u=p[i].v; p[i^1].v=p[i].u; p[i^1].w=p[i].w; } init(); qsort(p,m*2,sizeof(p[0]),cmp); for(i=0;i<m*2;i++) add(p[i].u,p[i].v,p[i].w); spfa(1,n); init(); for(i=2;i<=n;i++) { add(i,p[i].u,p[i].w); add(p[i].u,i,p[i].w); } solve(); } }