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 }
还有一种贪心写法哦!