【bzoj 4016】[FJOI2014]最短路径树问题 树分治+树形dp

236 篇文章 0 订阅
47 篇文章 0 订阅

好开心,终于把这一道题a了,主要是才学了树分治就来写这一道题真的有点吃不消,理解了好一会。

1.首先,对于树分治,最好是将其理解成一种思想而不是单纯的一种简单的算法,因此对于树分治的题(听学长说一般都是处理路径问题),你该怎么做就怎么做。例如这一道题,其实只要把数据给小一点就很容易想到树形dp,定义gg[i][0]:距离根节点为i的最长路径,

gg[i][1]距离根节点为i的最长路径的方案数。ff[i][0]:除开这一个子树外其他之前子树的最长路径(用于计算经过根节点与其他的子树节点相连的的路径计算)ff[i][1]:除开这一个子树外其他之前子树的最长路径的方案数。

然后就可以了啊,每一次枚举距离更新答案。

2.至于之前的处理最短树就不说了

//一开始忘了0的深度的情况 
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#define maxn 60020
using namespace std;
int n,m,k,f[maxn],head[maxn],tot=1,d[maxn],vis[maxn];
typedef pair<int ,int>pii;
priority_queue<pii,vector<pii>,greater<pii> >q;
vector<pii >g[maxn];
struct edge{
	int v,w,next;
}e[120020];
struct node{
	int a,b,w;
	bool operator<(const node& b)const{return w<b.w;}
}nod[maxn];
void adde(int a,int b,int c){
	e[tot].v=b;
	e[tot].w=c;
	e[tot].next = head[a];
	head[a] = tot++;
}
void dijkstra(){
	for(int i=0;i<=n;i++)d[i]=1e9;
	d[1]=0;
	vis[1]=1;
	q.push(make_pair(0,1));
	while(!q.empty()){
		int u=q.top().second;
		q.pop();
		vis[u]=1;
		for(int i=0;i<g[u].size();i++){
			int v=g[u][i].first;
			if(!vis[v]&&d[v]>d[u]+g[u][i].second){
				d[v]=d[u]+g[u][i].second;
				q.push(make_pair(d[v],v));
			}
		}
	}
	return;
}
bool build_vis[maxn];
void build(int u){
	build_vis[u]=true;
	sort(g[u].begin(),g[u].end());
	for(int i=0;i<g[u].size();i++){
		int v=g[u][i].first,w=g[u][i].second;
		if(d[v]==d[u]+w&&!build_vis[v]){
			adde(v,u,w);
			adde(u,v,w);
			build(v);
		}
	}
}

int rt,ff[maxn][3],s[maxn],gg[maxn][3],size,ans1=0,ans2=0,h[maxn],dis[maxn];//gg[i][0]表示以当前i为重心子树为重心的时候包含k个节点的最优解 
//gg[i][1] 表示以i为重心的时候并且求得最长路径的的时候的路径数 

void getroot(int u,int fa){
	f[u]=0,s[u]=1;
	for(int i=head[u];~i;i=e[i].next ){
		int v=e[i].v ;
		if(v==fa||vis[v])continue;
		getroot(v,u);
		s[u]+=s[v];
		f[u]=max(f[u],s[v]);
	}
	f[u]=max(f[u],size-f[u]);//我去 忘了更新这里的f 
	if(f[u]<f[rt])rt=u;//写成了大于 
}

void dfs(int u,int fa){
	
	if(gg[h[u]][0]<dis[u]){
		gg[h[u]][0]=dis[u];
		gg[h[u]][1]=1;
	}else if(gg[h[u]][0]==dis[u])gg[h[u]][1]++;
	
	for(int i=head[u];~i;i=e[i].next ){
		int v=e[i].v,w=e[i].w ;
		if(vis[v]||v==fa)continue;
		h[v]=h[u]+1;dis[v]=dis[u]+w;
		dfs(v,u);
	}
}

void calc(int u){
	
	for(int i=1;i<=k;i++)ff[i][0]=0,ff[i][1]=0;
	ff[0][1]=1;
	for(int i=head[u];~i;i=e[i].next ){
		
		int v=e[i].v;
		if(vis[v])continue;
		
		for(int j=1;j<=k;j++)gg[j][0]=0,gg[j][1]=0;
		gg[0][1]=1;
		h[u]=0;
		h[v]=1,dis[v]=e[i].w;
		dfs(v,u);
		
		for(int j=1;j<k;j++){
			int w;//找最长的路径
			w= gg[j][0]+ff[k-j-1][0];
			if(w>ans1)ans1=w,ans2=gg[j][1]*ff[k-j-1][1];
			else if(w==ans1){
				ans2+=gg[j][1]*ff[k-j-1][1];
			}
		}
		for(int j=1;j<k;j++){
			if(ff[j][0]<gg[j][0]){
				ff[j][0]=gg[j][0];
				ff[j][1]=gg[j][1];
			}else if(ff[j][0]==gg[j][0]){
				ff[j][1]+=gg[j][1];
			}
		}
	}
}

void solve(int u){
	vis[u]=1;
	calc(u);
	
	for(int i=head[u];~i;i=e[i].next ){
		int v=e[i].v;
		if(vis[v])continue;
		f[0]=size=s[v];rt=0;
		if(s[v]>=k-1){
			getroot(v,0);
			solve(rt);
		}
	}
}

int main(){
	memset(head,-1,sizeof(head));
	scanf("%d%d%d",&n,&m,&k);
	for(int a,b,c,i=1;i<=m;i++){
		scanf("%d%d%d",&a,&b,&c);
		g[a].push_back(make_pair(b,c));
		g[b].push_back(make_pair(a,c));
	}
	dijkstra();
	memset(vis,0,sizeof(vis));
	build(1);
	memset(vis,0,sizeof(vis));
	rt=0;f[0]=size=n;
	getroot(1,rt); 
	solve(rt);
	printf("%d %d",ans1,ans2);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值