【树形DP】CF1016F Road Projects

传送门

题解

一开始想的是先求出 (1,n) 的单源最短路,之后枚举中转点把两段拼起来,几乎写完了之后才发现我这个想法根本就不对。(因为没办法简单地把两段路径拼在一起)重构了,用时巨长。
其实,按照上面的思路继续,应该也不难想出正解。
变换一下视角,把 (1 - n) 的路径单独提出来,以后的操作基于这条链。
这条链上可能挂着一些子树,分类讨论。

  • 如果有大小超过 (2) 的子树(包括链上的那个点),就可以在子树内连边,不对答案造成影响。
  • 如果没有上述子树,说明链上顶多有一条边伸出去。此时继续分类讨论。
    • 对于每一个链上的点,都可以隔一个链上相邻的点连边。(按照贪心,不会隔着更多的点连边)
    • 对于每一个伸出去的点,都可以和链上相邻的两点连边。同时,也可以和前面其他伸出去的点连边。

细节略多,如果考试的话我八成写挂了吧...现在要细节再细节...

代码

#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f,N = 3e5+10;
#define ll long long int
inline ll read()
{
	ll ret=0;char ch=' ',c=getchar();
	while(!(c>='0'&&c<='9')) ch=c,c=getchar();
	while(c>='0'&&c<='9') ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
	return ch=='-'?-ret:ret;
}
int n,m;
int siz;
ll dis[N],val,lst;
int ecnt=-1,head[N];
int stk[N],top;
bool vis[N];
bool flag;
inline void init()
{
	memset(head,-1,sizeof(head));
	ecnt=-1;
}
struct edge
{
	int nxt,to,w;
}a[N<<1];
inline void add_edge(const int x,const int y,const int w)
{
	a[++ecnt]=(edge){head[x],y,w};
	head[x]=ecnt;
}
void find_chain(const int u,const int fa)
{
	if(!flag) stk[++top]=u,vis[u]=1;
	if(u==n) flag=1;
	for(int i=head[u];~i;i=a[i].nxt)
	{
		int v=a[i].to;
		if(v==fa) continue;
		dis[v]=dis[u]+a[i].w;
		find_chain(v,u);
	}
	if(!flag) top--,vis[u]=0;
}
void dfs(const int u)
{
	siz++,vis[u]=1;
	for(int i=head[u];~i;i=a[i].nxt)
	{
		int v=a[i].to;
		if(vis[v]) continue;
		val+=a[i].w;
		dfs(v);
	}
}
int main()
{
	init();
	n=read(),m=read();
	for(int i=1;i<n;i++)	
	{
		int u=read(),v=read(),w=read();
		add_edge(u,v,w),add_edge(v,u,w);
	}
	find_chain(1,-1);
	ll l=1e18;
	dis[0]=-1e18,dis[n+1]=1e18;
	stk[top+1]=n+1; //处理边界越界问题 
	for(int i=1;i<=top;i++)	
	{
	//	printf("stk[%d]=%d
",i,stk[i]);
	//	printf("dis[%d]=%lld
",stk[i],dis[stk[i]]);
		siz=val=0;
		if(i>2) l=min(l,dis[stk[i]]-dis[stk[i-2]]);
		dfs(stk[i]);
		if(siz>=3) {l=0LL;break;}
		if(val) l=min(l,min(dis[stk[i]]-dis[stk[i-1]]-val,dis[stk[i+1]]-dis[stk[i]]-val));//我tm无语,这个+1的边界越界调死我了 
		if(lst&&val) l=min(l,dis[stk[i]]-val-lst);
		if(val) lst=max(lst,val+dis[stk[i]]);
	}
	//printf("l=%lld
",l);
	for(int i=1;i<=m;i++)	
	{
		int x=read();
		printf("%lld
",min(dis[n],dis[n]+x-l));
	}
	return 0;
}
/*
8 5
7 3 2 
3 2 8
1 7 11
4 6 13
8 5 3
2 6 14
4 8 5
*/
原文地址:https://www.cnblogs.com/conprour/p/15468372.html