• 【BZOJ-4016】最短路径树问题 Dijkstra + 点分治


    4016: [FJOI2014]最短路径树问题

    Time Limit: 5 Sec  Memory Limit: 512 MB
    Submit: 1092  Solved: 383
    [Submit][Status][Discuss]

    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

    HINT

    对于所有数据n<=30000,m<=60000,2<=K<=n。

    数据保证最短路径树上至少存在一条长度为K的路径。

    2016.12.7新加数据一组by - wyx-150137

    Source

    Solution

    这道题还是比较好搞的。

    首先按照题目的意思搞出 最短路径树 来,这里利用vector排序搞了一下字典序的问题。

    然后就可以用 点分治 处理答案了。

    令$dp[dep][0/1]$表示经过点数为$dep$的最长路径的长度和方案数,然后DFS一遍就可以了。

    Code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    #include<queue>
    using namespace std;
    inline int read()
    {
    	int x=0,f=1; char ch=getchar();
    	while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    	while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    	return x*f;
    }
    #define MAXN 100010
    #define LL long long
    
    int N,M,K;
    LL Dist,Num;
    
    vector<int> G[MAXN];
    
    namespace Graph{
    	
    	struct EdgeNode{
    		int next,to,dis,from;
    	}edge[MAXN<<1];
    	int head[MAXN],cnt;
    
    	inline void AddEdge(int u,int v,int w) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v; edge[cnt].from=u; edge[cnt].dis=w;}
    	inline void InsertEdge(int u,int v,int w) {AddEdge(u,v,w); AddEdge(v,u,w);}
    
    	#define Pa pair<int,int>
    	#define MP make_pair
    	#define INF 0x7fffffff
    	priority_queue<Pa,vector<Pa>,greater<Pa> >q;
    	int dist[MAXN];
    	inline void Dijkstra(int S=1)
    	{
    		for (int i=1; i<=N; i++) dist[i]=INF;
    		q.push(MP(0,S)); dist[S]=0;
    		while (!q.empty()) {
    			int dis=q.top().first;
    			int now=q.top().second;
    			q.pop();
    			if (dis>dist[now]) continue;
    			for (int i=head[now]; i;i=edge[i].next) {
    				if (dist[edge[i].to]>dis+edge[i].dis) {
    					dist[edge[i].to]=dis+edge[i].dis;
    					q.push(MP(dist[edge[i].to],edge[i].to));
    				}
    			}
    		}
    		
    		for (int i=1; i<=cnt; i++) {
    			int u=edge[i].from,v=edge[i].to,d=edge[i].dis;
    			if (dist[u]+d==dist[v]) G[u].push_back(v);
    		}
    		
    //		for (int i=1; i<=N; printf("Now=%d
    ",i),i++)
    //			for (int j=0; j<G[i].size(); j++)
    //				printf("%d  ",G[i][j]);
    	}
    }
    
    namespace TreeDivide{
    
    	struct EdgeNode{
    		int next,to,dis;
    	}edge[MAXN<<1];
    	int head[MAXN],cnt;
    	
    	inline void AddEdge(int u,int v,int w) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v; edge[cnt].dis=w;}
    	inline void InsertEdge(int u,int v,int w) {/*printf("<%d  %d>
    ",u,v,w);*/ AddEdge(u,v,w); AddEdge(v,u,w);}
    	
    	int size[MAXN],mx[MAXN],root,Sz;
    	bool visit[MAXN];
    	inline void Getroot(int now,int last)
    	{
    		size[now]=1; mx[now]=0;
    		for (int i=head[now]; i; i=edge[i].next)
    			if (edge[i].to!=last && !visit[edge[i].to]) {
    				Getroot(edge[i].to,now);
    				size[now]+=size[edge[i].to];
    				mx[now]=max(mx[now],size[edge[i].to]);
    			}
    		mx[now]=max(mx[now],Sz-size[now]);
    		if (mx[now]<mx[root]) root=now;
    	}
    	
    	int f[MAXN][2],g[MAXN][2];
    	inline void DFS(int now,int last,int dep,int dis)
    	{
    		if (dep>K) return;
    		if (dis>f[dep][0]) 
    			f[dep][0]=dis,f[dep][1]=1;
    		else 
    			if (dis==f[dep][0]) 
    				f[dep][1]++;
    
    		for (int i=head[now]; i; i=edge[i].next)
    			if (edge[i].to!=last && !visit[edge[i].to]) {
    				DFS(edge[i].to,now,dep+1,dis+edge[i].dis);
    			}
    	}
    	
    	inline void Divide(int now)
    	{
    		visit[now]=1;
    		for (int i=1; i<=K; i++) g[i][0]=g[i][1]=0;
    		g[0][0]=0; g[0][1]=1;
    		
    		K--;
    		for (int i=head[now]; i; i=edge[i].next)
    			if (!visit[edge[i].to]) {
    				
    				for (int j=1; j<=K; j++) f[j][0]=f[j][1]=0;
    				
    				DFS(edge[i].to,now,1,edge[i].dis);
    				
    				for (int j=1; j<=K; j++) {
    					if (Dist<g[K-j][0]+f[j][0])
    						Dist=g[K-j][0]+f[j][0],Num=(LL)g[K-j][1]*f[j][1];
    					else
    						if (Dist==g[K-j][0]+f[j][0])
    							Num+=(LL)g[K-j][1]*f[j][1];
    				}
    				
    				for (int j=1; j<=K; j++) {
    					if (g[j][0]<f[j][0])
    						g[j][0]=f[j][0],g[j][1]=f[j][1];
    					else
    						if (g[j][0]==f[j][0])
    							g[j][1]+=f[j][1];
    				}
    			}
    		K++;
    		
    		
    		for (int i=head[now]; i; i=edge[i].next) 
    			if (!visit[edge[i].to]) {
    				root=0;
    				Sz=size[edge[i].to];
    				Getroot(edge[i].to,now);
    				Divide(root);
    			}
    	}
    	
    	bool mark[MAXN];
    	inline void BuildTree(int now)
    	{
    		mark[now]=1;
    		sort(G[now].begin(),G[now].end());
    		for (int i=0; i<G[now].size(); i++) 
    			if (!mark[G[now][i]]) {
    			BuildTree(G[now][i]);
    			InsertEdge(now,G[now][i],Graph::dist[G[now][i]]-Graph::dist[now]);
    		}
    	}
    }using namespace TreeDivide;
    
    int main()
    {
    	N=read(),M=read(),K=read();
    	for (int i=1,x,y,z; i<=M; i++) {
    		x=read(),y=read(),z=read();
    		Graph::InsertEdge(x,y,z);
    	}
    	Graph::Dijkstra();
    	TreeDivide::BuildTree(1);
    	
    	Sz=mx[root=0]=N;
    	TreeDivide::Getroot(1,0);
    	TreeDivide::Divide(root);
    
    	printf("%lld %lld
    ",Dist,Num);
    	return 0;
    }
    
  • 相关阅读:
    Linux 技巧:让进程在后台可靠运行的几种方法
    What is /dev/null 2>&1?
    In the shell, what does “ 2>&1 ” mean?
    Linux命令之umask
    /dev/null简介
    What is special about /dev/tty?
    sed用法
    cobbler 更换dns和dhcp服务器为dnsmasq
    Linux下如何退出vim的一些常用命令总结
    nginx部署vue项目
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/6339392.html
Copyright © 2020-2023  润新知