CF1016F:Road Projects(树形dp)

解析

好题
意思就是我没做出来
稍微分析一下就可以发现加边的位置始终是一样的
换句话说询问完全可以O1
关键就是找到这条边加在哪里

一开始我完全把这道题看成了彻头彻尾的数据结构题
容易想到二分答案
然后上个树状树组搞一搞就行了
但是遇到一个关键的问题
它无法解决加边必须是非树边的问题
于是又搞巴搞巴整了个线段树并且把离散化的去重干掉,开始乱搞
然后就T了…
本来线段树常数就大我的那个东西暴力修改还自带好几倍常数…
俩log根本过不去3e5

无奈之下看了题解
真的很巧妙
复杂度和实现难度双重碾压我qwq
把1-n的链提出来
然后分类一下就迎刃而解了
upd:代码有问题!(CF没卡掉…但是显然是假的)
求链上的min时不一定相邻,而是应该提出来一些东西维护一个最小值

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define il inline
#define debug(a,b) fprintf(stderr,a,b)
const int N=3e5+100;
const int M=3e6+100;
const int mod=998244353;
inline ll read(){
  ll x=0,f=1;char c=getchar();
  while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
  while(isdigit(c)){x=x*10+c-'0';c=getchar();}
  return x*f;
}
int n,m;
int fi[N],cnt;
struct node{
  int to,nxt,w;
}p[N<<1];
inline void addline(int x,int y,int w){
  p[++cnt]=(node){y,fi[x],w};fi[x]=cnt;
  return;
}
ll ans=3e14;
int fa[N],siz[N],hson[N];
ll dis[N];
void dfs(int x,int f){
  fa[x]=f;siz[x]=1;
  for(int i=fi[x];~i;i=p[i].nxt){
    int to=p[i].to;
    if(to==f) continue;
    dis[to]=dis[x]+p[i].w;
    dfs(to,x);
    siz[x]+=siz[to];
  }
  return;
}
ll add[N];
void find(int x){
  if(!x) return;
  if(fa[fa[x]]){
    ans=min(ans,dis[x]-dis[fa[fa[x]]]);
  }
  hson[fa[x]]=x;
  if(siz[x]-siz[hson[x]]-1>=2){
    ans=0;return;
  }
  for(int i=fi[x];~i;i=p[i].nxt){
    int to=p[i].to;
    if(to==fa[x]||to==hson[x]) continue;
    add[x]=p[i].w;
  }
  for(int i=fi[x];~i;i=p[i].nxt){
    int to=p[i].to;
    if(to!=hson[x]) continue;
    if(add[x]||add[to]) ans=min(ans,1ll*p[i].w-add[x]-add[to]);
  }
  find(fa[x]);
}
int main(){
  #ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
  #endif
  memset(fi,-1,sizeof(fi));cnt=-1;
  n=read();m=read();
  for(int i=1;i<n;i++){
    int x=read(),y=read(),w=read();
    addline(x,y,w);addline(y,x,w);
  }
  dfs(1,0);
  find(n);
  for(int i=1;i<=m;i++){
    int x=read();
    printf("%lld\n",dis[n]-max(0ll,ans-x));
  }
  return 0;
}
/*
3 1
3 1 3

3 2
1 1 2
3 1 3
*/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值