题目描述:
给出一棵树,n~1e5,然后给出m~1e5条特殊的链,每个链有一个权值.现在在树上挑选不相交的链,使得最后的拿到的权值最大.
题解:
看一个点,是树形dp,但是发现如果u是root,枚举过u的链,发现要暴力求和链上周围的点的dp和.这样会超时. 于是想到快速求一条链的和. 为了好写,我们把求链上连接的儿子的dp值的和转化到链上的权值. sum[u]指u的所有儿子的dp和,这个好想,那么一条链上的其实就是(sum[u] - 所有链上儿子d+链上儿子的sum-链上儿子儿子的d….),总结就是链上除了root,其他的点权值都是sum[v]-d[v].而root的权值是sum[root]. 我们是算d[u]的时候才用的,当时d[u]肯定就是0. 所以求链上的权值和. 我们有两种求法
(1)邓爷教的标序+lca. 这样求的是路径,我们用一种常用的方法,把i的值归到i到他fa的那条边上. 这样我们求路径+sum[root]就行了.
(2)用树链剖分. 映射到树状数组上,每次求和.
重点:
(1)关键是求一条链上的和.
(2)把权值归到链上,简化写法.
(3)点的权值归到u到fa的边上.
(4)标序或者树链剖分求链的和
(5)标序+st表求lca
代码:
//这个是标序算链的和.
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(int i = a;i < b;i++)
#define REP_D(i, a, b) for(int i = a;i <= b;i++)
typedef long long ll;
using namespace std;
const int maxn = 1e5 + 100;
const int maxRMQ = 4e5 + 100;
const int maxIND = 2e5 + 1000;
const int maxTree = 1e6 + 100;
int nn, m;
struct Edge
{
int a, b, val;
};
vector<Edge> edge[maxn];
vector<int> G[maxn];
int st[maxRMQ][50], L2[maxRMQ], P2[50];
int dfn, index[maxn], fa_index[maxn], first_u[maxn];
int a_st[maxRMQ], an;
int dfx, getin[maxn], getout[maxn];
int d[maxn], sum[maxn];
//void pushUp(int rt)
//{
// int lRt = (rt<<1), rRt = ((rt<<1)|1);
// tree[rt] = tree[lRt]+tree[rRt];
//}
//void change(int pos, int key, int rt, int l, int r)
//{
// if(l==r && l == pos)//主义同时修改tree
// {
// tree[rt] = key;
// return;
// }
// pushDown(rt);
// int mid = ((l + r)>>1), lRt = (rt<<1), rRt = ((rt<<1)|1);
// if(pos <= mid)
// {
// change(pos,key, lRt, l, mid);
// }
// if(pos >= mid + 1)
// {
// change(pos, key, rRt, mid + 1, r);
// }
// pushUp(rt);//向上push
//}
//
//int query(int L, int R, int rt, int l, int r)
//{
// if(L <= l && R >= r)//全包括
// {
// return tree[rt];
// }
// int ans =0, mid = ((l + r)>>1), lRt = (rt<<1), rRt = ((rt<<1)|1);
// if(L <= mid)
// {
// ans += query(L, R, lRt, l, mid);
// }
// if(R >= mid + 1)
// {
// ans += query(L, R, rRt, mid + 1, r);
// }
// return ans;
//}
void dfs_dfn(int u, int fa)//这是lca
{
dfn++;
index[u] = dfn;
fa_index[dfn] = u;
an++;
a_st[an] = dfn;
first_u[u] = an;
REP(i, 0, G[u].size())
{
int v = G[u][i];
if(v!=fa)
{
dfs_dfn(v, u);
an++;
a_st[an] = index[u];
}
}
}
void getL2()
{
L2[1] = 0;
for(int i = 2; i<=400000; i++)
{
if((i&(i-1))==0)
{
L2[i] = L2[i-1]+1;
}
else
{
L2[i] = L2[i-1];
}
//printf("%d %d\n", i, L2[i]);
}
}
void initail()
{
P2[0] = 1;
for(int i = 1; i<=30; i++)
{
P2[i] = 2*P2[i-1];
}
for(int i = 1; i <= an; i++)
{
st[i][0] = a_st[i];
//printf(" i is %d %d\n", i, a_st[i]);
}
for(int s = 1; s<=30; s++)
{
for(int i = 1; i+P2[s] - 1 <=an; i++)
{
int j = i+P2[s-1];
st[i][s] = min(st[i][s-1], st[j][s-1]);
}
}
}
int st_query(int a, int b)
{
int len = (b-a+1);
int s = L2[len];
return min(st[a][s], st[b-P2[s]+1][s]);
}
int getLca(int a, int b)//lca结束
{
int l = first_u[a], r = first_u[b];
if(l > r)
{
swap(l, r);
}
return fa_index[st_query(l, r)];
}
int tree[maxn << 1];//树状数组
int getsum(int x)
{
int ret = 0;
while (x)
{
ret += tree[x];
x -= (x & (-x));
}
return ret;
}
void update(int x, int y)
{
while (x <= dfx)
{
tree[x] += y;
x += (x & (-x));
}
}
void dfs_xu(int u, int fa)
{
dfx++;
getin[u] = dfx;
REP(i, 0, G[u].size())
{
int v = G[u][i];
if(v!=fa)
{
dfs_xu(v, u);
}
}
dfx++;
getout[u] = dfx;
}
int getT(int a, int b)
{
if(a==1)
{
return getsum(b);
}
return getsum(b)-getsum(a-1);
}
int getSum_t(int a, int b, int lca)
{
int l = getin[lca];
int r = getin[a];
int ans = 0;
ans += getT(l, r);
r = getin[b];
ans += getT(l, r);
return ans;
}
void dfs(int u, int fa)//树形dp,求和链上.
{
sum[u] = 0;
REP(i, 0, G[u].size())
{
int v = G[u][i];
if(v!=fa)
{
dfs(v, u);
sum[u] += d[v];
}
}
d[u] = sum[u];
REP(i, 0, edge[u].size())
{
Edge e = edge[u][i];
int a = e.a, b = e.b, val = e.val;
// if(u == 2)
// {
// for(int i = 1; i<=dfx; i++)
// {
// printf("tree i is %d %d\n", i, tree[i]);
// }
// }
int tmp = getSum_t(a, b, u);
d[u] = max(d[u], tmp + sum[u] + val);
}
//printf("%d %d %d\n", u, sum[u], d[u]);
update(getin[u], sum[u]-d[u]);
update(getout[u], d[u]-sum[u]);
//change(getin[u], sum[u]-d[u], 1, 1, dfx);
//change(getout[u], d[u]-sum[u], 1, 1, dfx);
}
void solve()
{
REP_D(i, 1, nn)
{
G[i].clear();
edge[i].clear();
}
REP_D(i, 1, nn - 1)
{
int a, b;
scanf("%d%d", &a, &b);
G[a].push_back(b);
G[b].push_back(a);
}
dfn = 0;
an = 0;
dfs_dfn(1, 0);
initail();
REP_D(i, 1, m)
{
int a, b, val;
scanf("%d%d%d", &a, &b, &val);
int lca = getLca(a, b);
Edge t;
t.a = a;
t.b = b;
t.val = val;
edge[lca].push_back(t);
//printf("lca %d %d %d\n", a, b, lca);
}
CLR(tree);
CLR(sum);
CLR(d);
dfx = 0;
dfs_xu(1,0);
dfs(1, 0);
printf("%d\n", d[1]);
}
int main()
{
// freopen("13Min.txt", "r", stdin);
//freopen("1out.txt", "w", stdout);
int t;
getL2();
scanf("%d", &t);
while(t--)
{
scanf("%d%d", &nn,&m);
solve();
}
return 0;
}
//这个是树链剖分,但是线段树要re....所以没有过
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(int i = a;i < b;i++)
#define REP_D(i, a, b) for(int i = a;i <= b;i++)
typedef long long ll;
using namespace std;
const int maxn = 1e5 + 100;
const int maxLMQ = 4*maxn+100;
const int MAXL = 30;
int st[maxLMQ][MAXL];
int an, a[maxLMQ];
int index[maxn], fa_index[maxn], first_byindex[maxn];
int dfn;
vector<int> G[maxn];
struct Edge
{
int a, b, val, lca;
};
Edge edge[maxn];
vector<Edge> lca_edge[maxn];
int edge_n;
int P2[maxn], L2[maxn];
int d[maxn], sum[maxn];
int dep[maxn], w[maxn], fa[maxn], top[maxn], son[maxn], siz[maxn];
int z;
int tree[maxLMQ];
int n;
void getP2()
{
P2[0] = 1;
for(int i = 1; i<=30; i++)
{
P2[i] = P2[i-1]*2;
}
}
void getL2()
{
L2[1] = 0;
for(int i = 2; i<=400000; i++)
{
if((i&(i-1))==0)
{
L2[i] = L2[i-1]+1;
}
else
{
L2[i] = L2[i-1];
}
//printf("%d %d\n", i, L2[i]);
}
}
void initail()
{
for(int i = 1; i <= an; i++)
{
st[i][0] = a[i];
}
for(int s = 1; s<=30; s++)
{
for(int i = 1; i<= an; i++)
{
int j = i+P2[s-1];
if(j>=an)
continue;
st[i][s]= min(st[i][s-1],st[j][s-1]);
}
}
}
int query(int l, int r)
{
int len = (r-l+1);
int s = L2[len];
int tmp = P2[s];
return min(st[l][s], st[r - tmp + 1][s]);
}
int getLca(int a, int b)
{
a = index[a];
b = index[b];
int l = first_byindex[a], r = first_byindex[b];
if(l > r)
{
swap(l, r);
}
return fa_index[query(l, r)];
}
void dfs_dfn(int u, int fa)
{
++an;
a[an] = dfn;
first_byindex[dfn] = an;
index[u] = dfn;
fa_index[dfn] = u;
dfn++;
REP(i, 0, G[u].size())
{
int v = G[u][i];
if(v!=fa)
{
dfs_dfn(v, u);
an++;
a[an] = index[u];
}
}
}
void getEdge()
{
REP_D(i, 1, n)
{
lca_edge[i].clear();
}
REP(i, 0, edge_n)
{
scanf("%d%d%d", &edge[i].a, &edge[i].b, &edge[i].val);
edge[i].lca = getLca(edge[i].a, edge[i].b);
lca_edge[edge[i].lca].push_back(edge[i]);
}
}
void dfs_1(int u, int pat)//先准备
{
siz[u] = 1;
son[u] = 0;
REP(i, 0, G[u].size())
{
int v = G[u][i];
if(v!=pat)
{
fa[v] = u;
dep[v] = dep[u]+1;
dfs_1(v, u);
if(son[u]==0)
{
son[u] = v;
}
else if(siz[v] > siz[son[u]])
{
son[u] = v;
}
siz[u] += siz[v];
}
}
}
void dfs_2(int u, int pat, int tp)//标号边
{
if(pat != 0)
{
z++;
w[u] = z;
}
top[u] = tp;
if(son[u] !=0)
{
dfs_2(son[u], u, top[u]);
}
REP(i, 0, G[u].size())
{
int v = G[u][i];
if(v!=pat&&v!=son[u])
{
dfs_2(v, u, v);
}
}
}
int query_tree(int LL, int RR, int rt, int l, int r)
{
if(LL<=l && RR>= r)
{
return tree[rt];
}
int ans = 0;
int mid = ((l + r)>>1), lRt = (rt<<1), rRt = ((rt<<1)|1);
if(LL<=mid)
{
ans += query_tree(LL, RR, lRt, l, mid);
}
if(RR>=mid+1)
{
ans += query_tree(LL, RR, rRt, mid+1, r);
}
return ans;
}
void pushUp(int rt)
{
int lRt = (rt<<1), rRt = ((rt<<1)|1);
tree[rt] = tree[lRt]+ tree[rRt];
}
void update(int pos, int x, int rt, int l, int r)
{
if(pos == l && pos == r)
{
tree[rt] = x;
return;
}
int mid = ((l + r)>>1), lRt = (rt<<1), rRt = ((rt<<1)|1);
if(pos <= mid)
{
update(pos, x, lRt, l, mid);
}
else
{
update(pos , x, rRt, mid +1, r);
}
pushUp(rt);
}
int getSum(int a, int b)
{
int f1 = top[a], f2 = top[b];
int ans = 0;
while(f1 != f2)
{
if(dep[f1] < dep[f2])
{
swap(f1, f2);
swap(a, b);
}
ans += (query_tree(w[f1], w[a], 1, 1, z));
a = fa[f1];
f1 = top[a];
}
if(a==b)
return ans;
if(dep[a] > dep[b])
{
swap(a, b);
}
ans += query_tree(w[son[a]], w[b], 1, 1, z);
return ans;
}
void dfs(int u, int pat)
{
sum[u]= 0;
REP(i, 0, G[u].size())
{
int v = G[u][i];
if(v!=pat)
{
dfs(v, u);
sum[u] += d[v];
}
}
d[u] = sum[u];
REP(i, 0, lca_edge[u].size())
{
Edge &e = lca_edge[u][i];
int a = e.a, b = e.b, val = e.val;
int temp = getSum(e.a, e.b);
d[u]=max(d[u], temp+sum[u]+val);
}
if(u!=1)
{
update(w[u], sum[u]-d[u], 1, 1, z);
}
//printf("%d %d %d \n", u, sum[u], d[u]);
}
void solve()
{
CLR(tree);
dfn = 1;
an = 0;
dfs_dfn(1, 0);
initail();
getEdge();
dep[1] = 0;
dfs_1(1, 0);
z = 0;
dfs_2(1, 0, 1);
dfs(1, 0);
printf("%d\n", d[1]);
}
int main()
{
//freopen("6Fin.txt", "r", stdin);
//freopen("6Fout.txt", "w", stdout);
getP2();
getL2();
int t;
scanf("%d", &t);
while(t--)
{
scanf("%d%d", &n, &edge_n);
REP_D(i, 1, n)
{
G[i].clear();
}
REP_D(i, 1, n-1)
{
int a, b;
scanf("%d%d", &a, &b);
G[a].push_back(b);
G[b].push_back(a);
}
solve();
}
return 0;
}