zoukankan      html  css  js  c++  java
  • 【树形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
    */
    
  • 相关阅读:
    PDO 数据访问抽象层
    注册审核、批量删除
    分页查询
    会话用法
    封装成类
    多条件查询(复选框条件)
    IP子网划分
    redhat 用yum安装的apache、mysql一般默认安装在哪个目录下?
    nslookup
    linux 设置时间
  • 原文地址:https://www.cnblogs.com/conprour/p/15468372.html
Copyright © 2011-2022 走看看