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();
    }
    
  • 相关阅读:
    Unique Binary Search Trees——LeetCode
    Binary Tree Inorder Traversal ——LeetCode
    Maximum Product Subarray——LeetCode
    Remove Linked List Elements——LeetCode
    Maximum Subarray——LeetCode
    Validate Binary Search Tree——LeetCode
    Swap Nodes in Pairs——LeetCode
    Find Minimum in Rotated Sorted Array——LeetCode
    Linked List Cycle——LeetCode
    VR AR MR
  • 原文地址:https://www.cnblogs.com/suika/p/9062416.html
Copyright © 2011-2022 走看看