zoukankan      html  css  js  c++  java
  • BZOJ_4016_[FJOI2014]最短路径树问题_最短路+点分治

    BZOJ_4016_[FJOI2014]最短路径树问题_最短路+点分治

    Description

    给一个包含n个点,m条边的无向连通图。从顶点1出发,往其余所有点分别走一次并返回。
    往某一个点走时,选择总长度最短的路径走。若有多条长度最短的路径,则选择经过的顶点序列字典序最小的那条路径(如路径A为1,32,11,路径B为1,3,2,11,路径B字典序较小。注意是序列的字典序的最小,而非路径中节点编号相连的字符串字典序最小)。到达该点后按原路返回,然后往其他点走,直到所有点都走过。
    可以知道,经过的边会构成一棵最短路径树。请问,在这棵最短路径树上,最长的包含K个点的简单路径长度为多长?长度为该最长长度的不同路径有多少条?
    这里的简单路径是指:对于一个点最多只经过一次的路径。不同路径是指路径两端端点至少有一个不同,点A到点B的路径和点B到点A视为同一条路径。

    Input

    第一行输入三个正整数n,m,K,表示有n个点m条边,要求的路径需要经过K个点。接下来输入m行,每行三个正整数Ai,Bi,Ci(1<=Ai,Bi<=n,1<=Ci<=10000),表示Ai和Bi间有一条长度为Ci的边。数据保证输入的是连通的无向图。

    Output

    输出一行两个整数,以一个空格隔开,第一个整数表示包含K个点的路径最长为多长,第二个整数表示这样的不同的最长路径有多少条。

    Sample Input

    6 6 4
    1 2 1
    2 3 1
    3 4 1
    2 5 1
    3 6 1
    5 6 1

    Sample Output

    3 4

    首先要理解题中‘字典序最小的含义’。
    跑一边dij,对每个点开vector,如果更新前dis[to]==dis[x]+val[i]说明这条反边在最短路图上,就把这条反边加入to的vector中。
    于是我们就有了一个最短路图,对每个vector进行排序,然后求出整个图的dfs树就是题中所求的最短路树。
    然后就是一个点分治可做的问题了。
    维护出当前深度下最长路径和最长路径的条数即可。
     
    代码:
    #include <stdio.h>
    #include <string.h>
    #include <algorithm>
    #include <stdlib.h>
    #include <vector>
    #include <ext/pb_ds/priority_queue.hpp>
    using namespace std;
    using namespace __gnu_pbds;
    #define N 30050
    #define M 60050
    #define RR register
    inline char nc() {
    	static char buf[100000],*p1,*p2;
    	return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    }
    inline int rd() {
    	register int x=0;
    	register char s=nc();
    	while(s<'0'||s>'9') s=nc();
    	while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+s-'0',s=nc();
    	return x;
    }
    int n,m,vis[N],dis[N],dfn[N],K;
    __gnu_pbds::priority_queue<pair<int,int> >q;
    vector<int>v[N];
    int head[N],to[M<<1],nxt[M<<1],val[M<<1],cnt=1;
    __attribute__((optimize("-O2")))void add(int u,int v,int w) {
    	to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; val[cnt]=w;
    }
    __attribute__((optimize("-O2")))void dij() {
    	memset(dis,0x3f,sizeof(dis));
    	dis[1]=0; q.push(make_pair(0,1));
    	while(!q.empty()) {
    		RR int x=q.top().second,i; q.pop();
    		if(vis[x]) continue;
    		vis[x]=1;
    		for(i=head[x];i;i=nxt[i]) {
    			if(dis[to[i]]+val[i]==dis[x]) {
    				// puts("FUCK");
    				v[to[i]].push_back(i^1);
    			}
    		}
    		for(i=head[x];i;i=nxt[i]) {
    			if(dis[to[i]]>dis[x]+val[i]) {
    				dis[to[i]]=dis[x]+val[i];
    				q.push(make_pair(-dis[to[i]],to[i]));
    			}
    		}
    	}
    }
    __attribute__((optimize("-O2")))inline bool cmp(int x,int y) {return to[x]<to[y];}
    struct Fenzhi {
    	int head[N],to[M<<1],nxt[M<<1],val[M<<1],cnt,used[N],sum,ans,tot;
    	int f[N],siz[N],root,d[N],dep[N],maxdep,maxmaxdep;
    	int g[N],h[N],t1[N],t2[N];//g[i]表示深度为i的点到根路径的最大值,h[i]为对应的方案数。
    	__attribute__((optimize("-O2")))void add(int u,int v,int w) {
    		to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; val[cnt]=w;
    	}
    	__attribute__((optimize("-O2")))void get_root(int x,int y) {
    		RR int i; siz[x]=1; f[x]=0;
    		for(i=head[x];i;i=nxt[i]) {
    			if(to[i]!=y&&!used[to[i]]) {
    				get_root(to[i],x);
    				siz[x]+=siz[to[i]];
    				f[x]=max(f[x],siz[to[i]]);
    			}
    		}
    		f[x]=max(f[x],sum-siz[x]);
    		if(f[x]<f[root]) root=x;
    	}
    	__attribute__((optimize("-O2")))void get_dep(int x,int y) {
    		maxdep=max(maxdep,dep[x]);	
    		siz[x]=1;
    		RR int i;
    		for(i=head[x];i;i=nxt[i]) {
    			if(to[i]!=y&&!used[to[i]]) {
    				d[to[i]]=d[x]+val[i];
    				dep[to[i]]=dep[x]+1;
    				if(t1[dep[to[i]]]<d[to[i]]) {
    					t1[dep[to[i]]]=d[to[i]]; t2[dep[to[i]]]=1;
    				}else if(t1[dep[to[i]]]==d[to[i]]) {
    					t2[dep[to[i]]]++;
    				}
    				get_dep(to[i],x);
    				siz[x]+=siz[to[i]];
    			}
    		}
    	}
    	__attribute__((optimize("-O2")))void work(int x) {
    		maxmaxdep=0;
    		used[x]=1;
    		RR int i,j;
    		for(i=head[x];i;i=nxt[i]) {
    			if(!used[to[i]]) {
    				maxdep=1;
    				d[to[i]]=val[i];
    				dep[to[i]]=1; t1[1]=val[i]; t2[1]=1;
    				get_dep(to[i],x);
    				for(j=1;j<=maxdep&&j<=K;j++) {
    					if(ans<t1[j]+g[K-j]) ans=t1[j]+g[K-j],tot=t2[j]*h[K-j];
    					else if(ans==t1[j]+g[K-j]) tot+=t2[j]*h[K-j];
    				}
    				for(j=1;j<=maxdep&&j<=K;j++) {
    					if(t1[j]>g[j]) g[j]=t1[j],h[j]=t2[j];
    					else if(t1[j]==g[j]) h[j]+=t2[j];
    				}
    				for(j=1;j<=maxdep;j++) t1[j]=-1<<30,t2[j]=0;
    				maxmaxdep=max(maxmaxdep,maxdep);
    			}
    		}
    
    		for(i=1;i<=maxmaxdep&&i<=K;i++) g[i]=-1<<30,h[i]=0;
    		for(i=head[x];i;i=nxt[i]) {
    			if(!used[to[i]]) {
    				root=0;
    				sum=siz[to[i]];
    				get_root(to[i],x);
    				work(root);
    			}
    		}
    	}
    	__attribute__((optimize("-O2")))void solve() {
    		sum=n; root=0; f[0]=1<<30;
    		get_root(1,0); work(root);
    		printf("%d %d
    ",ans,tot);
    	}
    }F;
    __attribute__((optimize("-O2")))void dfs(int x) {
    	RR int i;
    	dfn[x]=1;
    	sort(v[x].begin(),v[x].end(),cmp);
    	for(i=0;i<(int)v[x].size();i++) {
    		int t=v[x][i],c=val[t],v=to[t];
    		if(!dfn[v]) {
    			dfn[v]=1; F.add(x,v,c); F.add(v,x,c); dfs(v);
    		}
    	}
    }
    __attribute__((optimize("-O2")))int main() {
    	n=rd(); m=rd(); K=rd(); K--;
    	RR int i,x,y,z;
    	for(i=1;i<=m;i++) {
    		x=rd(); y=rd(); z=rd();
    		add(x,y,z); add(y,x,z);
    	}
    	dij();
    	dfs(1);
    	F.h[0]=1;
    	F.solve();
    }
    
  • 相关阅读:
    boost lexical_cast 字符串数字间的字面转换(学习笔记)
    Smoke Testing
    深入浅出InfoPath——手工绑定托管代码
    深入浅出InfoPath——操作InfoPath元素
    深入浅出SharePoint2010——附录:常用术语对照
    CAML语法必备
    深入浅出SharePoint ——2010 新特性之搜索
    BVT测试
    Windows 7的71个运行命令列表
    深入浅出InfoPath——如何在项目中引用GAC中的dll文件
  • 原文地址:https://www.cnblogs.com/suika/p/9062416.html
Copyright © 2011-2022 走看看