多校第一场1006
2015-07-23 22:31
169 查看
题目描述:
给出一棵树,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; }