多校第一场1006
来源:互联网 发布:vscode 大小写快捷键 编辑:程序博客网 时间:2024/06/05 04:58
题目描述:
给出一棵树,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;}
0 0
- 多校第一场1006
- 多校第一场
- 2017多校第一场 1006 Function
- 多校联赛第一场
- 2013多校第一场
- 2013 多校第一场
- 多校第一场 1003
- 多校第一场1010
- 2016多校第一场
- 2016多校第一场 1006 hdu 5728 PowMod
- hdu4728 PowMod(2016多校第一场1006)
- 2017多校第一场1006 function(HDU6038)
- 多校第一场:hdu 4308(bfs)
- 2013多校第一场 - from lanshui_Yang
- hdu 4602 Partition 多校第一场
- hdu 4604 Deque 多校第一场
- 杭电2013多校第一场
- 多校第一场Couple doubi
- 二叉堆
- 快速排序
- HDU 5301 Buildings(找规律)
- CentOS 6.3下配置软RAID(Software RAID)
- 关于CSS样式的笔记
- 多校第一场1006
- iOS开发分分钟搞定C语言 —— 数据类型
- 初涉iOS 通知机制
- topocoder Arena 客户端无法打开、下载jar包失败、jar包签名
- SVM源代码网站
- Linux PPTP VPN 记录登陆用户名
- 数字图像处理入门(一)-基本概念
- 递归与非递归及其相互转换
- Linux下只需3步轻松反编译Android APK