CF671D:Roads in Yusland

n<=300000个点的树,给m<=300000条带权路径(ui,vi,保证vi是ui的祖先)求覆盖整棵树每条边的最小权和。

好题好姿势!直观的看到可以树形DP,f[i]表示把点i包括它爸爸下面那条边都覆盖的最小权,就用经过他爸爸那条边的所有路径,各条路径加上一些子树信息来更新即可。

这样时间炸,那看看怎么优化。实际上我们不是在单纯地用一条路径更新答案,而是这样一个东西:

 

其中红色那条是题目给的路径,实际上是加上蓝色边连接的点的f[i]来更新边2上端的那个点的答案的。也就是说,一条路径来更新答案,要在这条路径的尾部加上那些点权(1),然后在更新某个点的答案的时候加上这个点下面的这条路径的分叉(2)。而更新一个点的路径,其实都是这个点对应子树的路径。至于子树中那些够不到这个点的路径,只需在扫到头的时候把这条路径的答案变inf即可。

为了实现这个操作,即找到“起点在子树里的所有路径的答案”,我们用dfs序给每个路径的起点(下端点)编号,再dfs求每个点的答案;每次求答案时,先把以该点为起点的新路径赋初值,即该点所有子树的f[j]和,并把终点在该点的路径答案置inf;然后给经过该点的路径加“分叉”;由于dfs序编号好了,能更新这个节点的路径(上面提到的起点在这个子树内的路径)是连续的一个区间,因此用个线段树维护区间min即可。

至于加“分叉”,观察可以发现:假如经过i的某路径来自子树j,那么应该把它答案加上点i其他儿子的f和。所以在加“分叉”时只需要再枚举一次孩子,把孩子子树内所有的路径加上其他孩子的f[j]和即可。

废话少说见代码!

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<stdlib.h>
  4 #include<algorithm>
  5 //#include<iostream>
  6 using namespace std;
  7 
  8 int n,m;
  9 #define maxn 300011
 10 struct Edge{int to,next;}edge[maxn*4];int first[maxn],start[maxn],end[maxn],le=2;
 11 void in(int x,int y,int* first) {edge[le].to=y;edge[le].next=first[x];first[x]=le++;}
 12 void insert(int x,int y,int* first) {in(x,y,first);in(y,x,first);}
 13 int dfn[maxn],Left[maxn],Right[maxn],Time=0;
 14 void dfs(int x,int fa)
 15 {
 16     Left[x]=Time+1;
 17     for (int i=start[x];i;i=edge[i].next)
 18     {
 19         Edge &e=edge[i];
 20         dfn[e.to]=++Time;
 21     }
 22     for (int i=first[x];i;i=edge[i].next)
 23     {
 24         Edge &e=edge[i];
 25         if (e.to!=fa) dfs(e.to,x);
 26     }
 27     Right[x]=Time;
 28 }
 29 #define LL long long
 30 LL val[maxn];
 31 const LL inf=1e15+233;
 32 struct SMT
 33 {
 34     struct Node
 35     {
 36         LL Min;
 37         LL add;
 38         int l,r;
 39         int ls,rs;
 40     }a[maxn*2];
 41     int size;
 42     SMT() {size=0;}
 43     void build(int &x,int L,int R)
 44     {
 45         x=++size;
 46         a[x].Min=inf;a[x].add=0;
 47         a[x].l=L;a[x].r=R;
 48         if (L==R) {a[x].ls=a[x].rs=0;}
 49         else
 50         {
 51             const int mid=(L+R)>>1;
 52             build(a[x].ls,L,mid);
 53             build(a[x].rs,mid+1,R);
 54         }
 55     }
 56     void build() {int x;build(x,1,m);}
 57     void up(int x)
 58     {
 59         const int &p=a[x].ls,&q=a[x].rs;
 60         a[x].Min=min(a[p].Min,a[q].Min);
 61     }
 62     void addsingle(int x,LL v)
 63     {
 64         a[x].Min+=v;
 65         a[x].Min=min(inf,a[x].Min);
 66         a[x].add+=v;
 67     }
 68     void down(int x)
 69     {
 70         const int &p=a[x].ls,&q=a[x].rs;
 71         if (a[x].add)
 72         {
 73             addsingle(p,a[x].add);
 74             addsingle(q,a[x].add);
 75             a[x].add=0;
 76         }
 77     }
 78     int ql,qr;LL v;
 79     void be(int x)
 80     {
 81         if (a[x].l==a[x].r) {a[x].Min=v;return;}
 82         down(x);
 83         const int mid=(a[x].l+a[x].r)>>1;
 84         if (ql<=mid) be(a[x].ls);
 85         if (ql> mid) be(a[x].rs);
 86         up(x);
 87     }
 88     void be(int p,LL v) {ql=qr=p;this->v=v;be(1);}
 89     void Add(int x)
 90     {
 91         if (ql<=a[x].l && a[x].r<=qr) {addsingle(x,v);return;}
 92         down(x);
 93         const int mid=(a[x].l+a[x].r)>>1;
 94         if (ql<=mid) Add(a[x].ls);
 95         if (qr> mid) Add(a[x].rs);
 96         up(x);
 97     }
 98     void Add(int L,int R,LL v) {if (L>R) return;ql=L;qr=R;this->v=v;Add(1);}
 99     LL query(int x)
100     {
101         if (ql<=a[x].l && a[x].r<=qr) return a[x].Min;
102         down(x);
103         const int mid=(a[x].l+a[x].r)>>1;LL ans=inf;
104         if (ql<=mid) ans=min(ans,query(a[x].ls));
105         if (qr> mid) ans=min(ans,query(a[x].rs));
106         return ans;
107     }
108     LL query(int L,int R) {if (L>R) return inf;ql=L;qr=R;return query(1);}
109 }t;
110 LL f[maxn];
111 void play(int x,int fa)
112 {
113     LL tot=0;
114     for (int i=first[x];i;i=edge[i].next)
115     {
116         Edge &e=edge[i];if (e.to==fa) continue;
117         play(e.to,x);
118         tot=min(inf,f[e.to]+tot);
119     }
120     for (int i=start[x];i;i=edge[i].next)
121     {
122         Edge &e=edge[i];
123         t.be(dfn[e.to],tot+val[e.to]);
124     }
125     for (int i=end[x];i;i=edge[i].next)
126     {
127         Edge &e=edge[i];
128         t.be(dfn[e.to],inf);
129     }
130     if (x==1) f[1]=tot;
131     else if (tot<inf)
132     {
133         for (int i=first[x];i;i=edge[i].next)
134         {
135             Edge &e=edge[i];if (e.to==fa) continue;
136             t.Add(Left[e.to],Right[e.to],tot-f[e.to]);
137         }
138         f[x]=t.query(Left[x],Right[x]);
139     }
140     else f[x]=inf;
141 }
142 void play()
143 {
144     dfs(1,0);
145     t.build();
146     play(1,0);
147 }
148 int x,y;LL v;
149 int main()
150 {
151     scanf("%d%d",&n,&m);
152     for (int i=1;i<n;i++)
153     {
154         scanf("%d%d",&x,&y);
155         insert(x,y,first);
156     }
157     for (int i=1;i<=m;i++)
158     {
159         scanf("%d%d%I64d",&x,&y,&val[i]);
160         in(x,i,start);
161         in(y,i,end);
162     }
163     play();
164     printf(f[1]>=inf?"-1\n":"%I64d\n",f[1]);
165     return 0;
166 }
View Code

还有一种贪心写法哦!

转载于:https://www.cnblogs.com/Blue233333/p/7480028.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值