dfs序基本类型总结
来源:互联网 发布:java p2p项目视频下载 编辑:程序博客网 时间:2024/06/05 20:50
大致看了下dfs序的题型,大致清楚了大致的解题思路。。。但是对于一些题目还是比较无力。。。。
dfs序比较重要的性质:一棵子树的所有节点在dfs序里是连续一段,主要就是利用这个性质来解题
题型一:对某个点X权值加上一个数W,查询某个子树X里所有点权值和。
解:列出dfs序,实现修改一个数,查询一段序列的和,显然这个序列可以用树状数组维护。
/*poj3321树状数组直接在第一次出现的位置+1,-1好了,对其他兄弟树没有影响,因为兄弟树是求区间,前面的+1,-1已经抵消掉了*/#include <iostream>#include <cstdio>#include <cmath>#include <algorithm>#include <cstring>#include <string>#define mem(a,b) memset(a,b,sizeof(a))using namespace std;typedef long long ll;const int maxn = 100005;struct ppp{int v,nex;}e[maxn * 4] ;int head[maxn],tole,n;void make_edge(int u,int v){e[tole].v = v;e[tole].nex = head[u];head[u] = tole++;}int qian[maxn],hou[maxn],vis[maxn];int sum[maxn * 2];int cnt_node;void init(){for(int i = 0;i < 2 * maxn;i++){head[i >> 1] = -1;sum[i] = 0;vis[i >> 1] = 1;}tole = 0;cnt_node = 0;}void dfs(int u,int pre){qian[u] = ++cnt_node;for(int i = head[u];~i;i = e[i].nex){int v = e[i].v;if(v == pre)continue;dfs(v,u);}hou[u] = ++cnt_node;}int lowbit(int x){return x & -x;}int query(int x){int ret = 0;while(x >= 1){ret += sum[x];x -= lowbit(x);}return ret;}void add(int x,int v){while(x <= cnt_node){sum[x] += v;x += lowbit(x);}}int main(){while(~scanf("%d",&n)){init();for(int i = 1,a,b;i <= n - 1;i++){scanf("%d%d",&a,&b);make_edge(a,b);make_edge(b,a);}dfs(1,-1);for(int i = 1;i <= n;i++){add(qian[i],1);}int q;scanf("%d",&q);char c;int a;while(q--){scanf(" %c%d",&c,&a);if(c == 'Q'){int now = query(hou[a]) - query(qian[a] - 1);printf("%d\n",now);}else {if(vis[a] > 0){add(qian[a],-1);}else {add(qian[a],1);}vis[a] = !vis[a];}}}}
题型二:对X到Y的最短路上所有点权值加上一个数W,查询某个点的权值。
解:并没有找到该类型的题目。。。。假设有这种题目吧。。若X到Y上的点的权值都加上W,那么其实就是X到根的权值加上W,Y到根的点权值加上W,lca(X,Y)到根的权值减去W,parent(lca(X,Y))到根的点权值减去W。
#include<bits/stdc++.h>#define mem(a,b) memset(a,b,sizeof(a))#define FOR(i,a,b) for(int i = a;i <= b;i++)using namespace std;typedef long long ll;const int maxn = 100005;const int INF = 0x3f3f3f3f;struct ppp{int v,nex;}e[maxn * 2];int head[maxn],q,n;int dp[maxn * 2][19],tole;int dep[maxn],pos[maxn],last[maxn * 2];int cnt;int st[maxn],ed[maxn];void make_edge(int u,int v){e[tole].v = v,e[tole].nex = head[u];head[u] = tole++;}int sum[maxn * 2],parent[maxn];inline int lowbit (int x){return x & -x;}void add(int x,int v){while(x <= cnt){sum[x] += v;x += lowbit(x);}}int que(int x){int ret = 0;while(x >= 1){ret += sum[x];x -= lowbit(x);}return ret;}void dfs(int u,int fa,int deep){parent[u] = fa;dep[u] = deep;pos[u] = ++cnt;st[u] = cnt;last[cnt] = u;int v;for(int i = head[u];~i;i = e[i].nex){v = e[i].v;if(v == fa)continue;dfs(v,u,deep + 1);pos[u] = ++cnt;last[cnt] = u;}ed[u] = cnt;}void ST(){for(int i = 1;i <= cnt;i++)dp[i][0] = last[i];for(int j = 1;(1 << j) <= cnt;j++)for(int i = 1;i + (1 << j) - 1 <= cnt;i++){dp[i][j] = dp[i][j - 1];if(dep[dp[i + (1 << (j - 1))][j - 1]] < dep[dp[i][j - 1]])dp[i][j] = dp[i + (1 << (j - 1))][j - 1];}}int query(int l,int r){int k = 0;while((1 << (k + 1)) <= r - l + 1)k++;if(dep[dp[l][k]] < dep[dp[r - (1 << k) + 1][k]])return dp[l][k];else return dp[r - (1 << k) + 1][k];}void init(){mem(head,-1);tole = 0;cnt = 0;mem(sum,0);}int main(){while(~scanf("%d",&n)){init();for(int i = 1,a,b;i <= n - 1;i++){scanf("%d%d",&a,&b);make_edge(a,b);make_edge(b,a);}dfs(1,-1,1);ST();scanf("%d",&q);//查询个数char c;int a,b;while(q--){scanf(" %c",&c);if(c == 'Q'){scanf("%d",&a);printf("%d\n",que(ed[a]) - que(st[a] - 1));}else {int v;scanf("%d%d%d",&a,&b,&v);int aa = pos[a],bb = pos[b];if(aa > bb)swap(aa,bb);int lca = query(aa,bb);add(st[a],v);//这里在树状数组上加加减减感觉挺难理解的,可以写出dfs序画一画add(st[b],v);add(st[lca],-v);if(lca != 1)add(st[parent[lca]],-v);}}}}
题型三:
从X到Y的点加上或者减去一个W,求某个点子树内的所有点的权值和
假设X在Y的子树内,那么对于询问点Y,询问值会加上W * (depth(X) - depth(Y) + 1)。
拆开后为W * (depth(x) + 1) - W * (depth(Y)) 。因为对于询问的是Y,由此可见此式子跟X无关,所以可以开设两个树状数组,一个保存W * (depth(x) + 1),另一个保存W * depth(Y),对于每一个部分维护,维护的方式类似题型二。
以下代码非本人写,供参考
#include <bits/stdc++.h>#define mem(a,b) memset(a,b,sizeof(a))using namespace std;typedef long long ll;const int MAXN = 1e5+10;vector<int> edge[MAXN];int s[2*MAXN];int s1[2*MAXN];int seq[2*MAXN];int seq1[2*MAXN];int depth[2*MAXN];int first[MAXN];int dp[2*MAXN][25];int st[MAXN];int ed[MAXN];int parent[MAXN];int cnt, num;int Lowbit(int x){ return x & (-x);}void Add(int x, int val, int n){ if(x <= 0) return; for(int i = x; i <= n; i += Lowbit(i)) { s[i] += val; }}void Add1(int x, int val, int n){ if(x <= 0) return; for(int i = x; i <= n; i += Lowbit(i)) { s1[i] += val; }}int Sum(int x){ int res = 0; for(int i = x; i > 0; i -= Lowbit(i)) { res += s[i]; } return res;}int Sum1(int x){ int res = 0; for(int i = x; i > 0; i -= Lowbit(i)) { res += s1[i]; } return res;}void Dfs(int u, int fa, int dep){ parent[u] = fa; seq[++cnt] = u; seq1[++num] = u; first[u] = num; depth[num] = dep; st[u] = cnt; int len = edge[u].size(); for(int i = 0; i < len; i++) { int v = edge[u][i]; if(v != fa) { Dfs(v, u, dep+1); seq1[++num] = u; depth[num] = dep; } } seq[++cnt] = u; ed[u] = cnt;}void RMQ_Init(int n){ for(int i = 1; i <= n; i++) { dp[i][0] = i; } for(int j = 1; (1 << j) <= n; j++) { for(int i = 1; i + (1 << j) - 1 <= n; i++) { int a = dp[i][j-1], b = dp[i + (1 << (j-1))][j-1]; dp[i][j] = depth[a] < depth[b] ? a : b; } }}int RMQ_Query(int l, int r){ int k = 0; while((1 << (k + 1)) <= r - l + 1) k++; int a = dp[l][k], b = dp[r-(1<<k)+1][k]; return depth[a] < depth[b] ? a : b;}int LCA(int u, int v){ int a = first[u], b = first[v]; if(a > b) a ^= b, b ^= a, a ^= b; int res = RMQ_Query(a, b); return seq1[res];}void Init(int n){ for(int i = 0; i <= n; i++) { edge[i].clear(); } memset(s, 0, sizeof(s));}int main(){ int n, op; int u, v, w; int cmd; while(scanf("%d %d", &n, &op) != EOF) { Init(n); for(int i = 0; i < n-1; i++) { scanf("%d %d", &u, &v); edge[u].push_back(v); edge[v].push_back(u); } cnt = 0, num = 0; Dfs(1, -1, 0); RMQ_Init(num); while(op--) { scanf("%d", &cmd); if(cmd == 0) { scanf("%d %d %d", &u, &v, &w); int lca = LCA(u, v);//以下维护跟求值其实跟题型二是一样的,就是分两个树状数组 Add(st[u], w * depth[first[u]] + w, cnt); Add1(st[u], w, cnt); Add(st[v], w * depth[first[v]] + w, cnt); Add1(st[v], w, cnt); Add(lca, -(w * depth[first[lca]] + w), cnt); Add1(lca, -w, cnt); Add(parent[lca], -(w * depth[first[parent[lca]]] + w), cnt); Add1(parent[lca], -w, cnt); } else if(cmd == 1) { scanf("%d", &u); printf("%d\n", Sum(ed[u]) - Sum(st[u] - 1) - depth[first[u]] * (Sum1(ed[u]) - Sum1(st[u] - 1))); } } } return 0;}
题型四:对于某个节点X加上一个W,查询X到Y路径上的所有点的权值和。ps:这个感觉比前面的都容易想到啊。。。。
那么加上一个W的维护就跟题型一的维护差不多。
查询的值就等于: X到根的权值和 + Y到跟的权值和 - LCA(X,Y)到根的权值和 - parent(LCA(X,Y))到根的权值和。
思路很简单,贴一下别人的代码:
const int MAXN = 1e5+10;vector<int> edge[MAXN];int s[2*MAXN];int s1[2*MAXN];int seq[2*MAXN];int seq1[2*MAXN];int depth[2*MAXN];int first[MAXN];int dp[2*MAXN][25];int st[MAXN];int ed[MAXN];int parent[MAXN];int cnt, num;int Lowbit(int x){ return x & (-x);}void Add(int x, int val, int n){ if(x <= 0) return; for(int i = x; i <= n; i += Lowbit(i)) { s[i] += val; }}int Sum(int x){ int res = 0; for(int i = x; i > 0; i -= Lowbit(i)) { res += s[i]; } return res;}void Dfs(int u, int fa, int dep){ parent[u] = fa; seq[++cnt] = u; seq1[++num] = u; first[u] = num; depth[num] = dep; st[u] = cnt; int len = edge[u].size(); for(int i = 0; i < len; i++) { int v = edge[u][i]; if(v != fa) { Dfs(v, u, dep+1); seq1[++num] = u; depth[num] = dep; } } seq[++cnt] = u; ed[u] = cnt;}void RMQ_Init(int n){ for(int i = 1; i <= n; i++) { dp[i][0] = i; } for(int j = 1; (1 << j) <= n; j++) { for(int i = 1; i + (1 << j) - 1 <= n; i++) { int a = dp[i][j-1], b = dp[i + (1 << (j-1))][j-1]; dp[i][j] = depth[a] < depth[b] ? a : b; } }}int RMQ_Query(int l, int r){ int k = 0; while((1 << (k + 1)) <= r - l + 1) k++; int a = dp[l][k], b = dp[r-(1<<k)+1][k]; return depth[a] < depth[b] ? a : b;}int LCA(int u, int v){ int a = first[u], b = first[v]; if(a > b) a ^= b, b ^= a, a ^= b; int res = RMQ_Query(a, b); return seq1[res];}void Init(int n){ for(int i = 0; i <= n; i++) { edge[i].clear(); } memset(s, 0, sizeof(s));}int main(){ int n, op; int u, v, w; int cmd; while(scanf("%d %d", &n, &op) != EOF) { Init(n); for(int i = 0; i < n-1; i++) { scanf("%d %d", &u, &v); edge[u].push_back(v); edge[v].push_back(u); } cnt = 0, num = 0; Dfs(1, -1, 0); RMQ_Init(num); while(op--) { scanf("%d", &cmd); if(cmd == 0) { scanf("%d %d", &u, &w); Add(st[u], w, cnt); Add(ed[u], -w, cnt); } else if(cmd == 1) { scanf("%d %d", &u, &v); int lca = LCA(u, v); printf("%d\n", Sum(st[u]) + Sum(st[v]) - Sum(st[lca]) - Sum(st[parent[lca]])); } } } return 0;}
题型五:对于一个点X的子树内的所有点加上一个值W,查询某个点的值。
那么只要在子树总都+w就好了,就是维护区间就是st[X] + w,ed[X] - w。
查询只要找que(X)就好了,注意如果点有原值的话要加上原值。
#include<bits/stdc++.h>#define mem(a,b) memset(a,b,sizeof(a))#define FOR(i,a,b) for(int i = a;i <= b;i++)using namespace std;typedef long long ll;const int maxn = 100005;const int INF = 0x3f3f3f3f;struct ppp{int v,nex;}e[maxn * 2];int head[maxn],q,n;int dp[maxn * 2][19],tole;int dep[maxn],pos[maxn],last[maxn * 2];int cnt;int st[maxn],ed[maxn];void make_edge(int u,int v){e[tole].v = v,e[tole].nex = head[u];head[u] = tole++;}int sum[maxn * 2],parent[maxn];inline int lowbit (int x){return x & -x;}void add(int x,int v){while(x <= cnt){sum[x] += v;x += lowbit(x);}}int que(int x){int ret = 0;while(x >= 1){ret += sum[x];x -= lowbit(x);}return ret;}void dfs(int u,int fa,int deep){parent[u] = fa;dep[u] = deep;pos[u] = ++cnt;st[u] = cnt;last[cnt] = u;int v;for(int i = head[u];~i;i = e[i].nex){v = e[i].v;if(v == fa)continue;dfs(v,u,deep + 1);pos[u] = ++cnt;last[cnt] = u;}ed[u] = cnt;}void ST(){for(int i = 1;i <= cnt;i++)dp[i][0] = last[i];for(int j = 1;(1 << j) <= cnt;j++)for(int i = 1;i + (1 << j) - 1 <= cnt;i++){dp[i][j] = dp[i][j - 1];if(dep[dp[i + (1 << (j - 1))][j - 1]] < dep[dp[i][j - 1]])dp[i][j] = dp[i + (1 << (j - 1))][j - 1];}}int query(int l,int r){int k = 0;while((1 << (k + 1)) <= r - l + 1)k++;if(dep[dp[l][k]] < dep[dp[r - (1 << k) + 1][k]])return dp[l][k];else return dp[r - (1 << k) + 1][k];}void init(){mem(head,-1);tole = 0;cnt = 0;mem(sum,0);}int main(){while(~scanf("%d",&n)){init();for(int i = 1,a,b;i <= n - 1;i++){scanf("%d%d",&a,&b);make_edge(a,b);make_edge(b,a);}dfs(1,-1,1);ST();scanf("%d",&q);//查询个数char c;int a,b;while(q--){scanf(" %c",&c);if(c == 'Q'){scanf("%d",&a);printf("%d\n",que(st[a]));//其中这个是更改后的值统计,如果有原值的话要加上原值}else {int w;scanf("%d%d",&a,&w);add(st[a],w);add(ed[a],-w);}}}}
题型六:对子树X里所有的节点加上一个值W,查询某个子树的权值和。
明显的区间修改,区间查询,那么。。。线段树。。
修改:就在st[X]到ed[X]上都加一个W
查询:直接查询st[X]到ed[X],记住都到的值除以2,因为每个点都算了两遍
代码就不挂了。
题型七:对于子树X内的所有点都加上一个值W,查询X到Y之间的路径上所有点权和。
那么这题就跟题型四很像,就是修改的时候改成修改一段区间,从X到Y路径上的话直接就套用题型四的做法。
贴一下别人的代码:
typedef struct { int l, r, sum, add;} Seg;const int MAXN = 1e5+10;Seg T[4*MAXN];vector<int> edge[MAXN];int s[2*MAXN];int s1[2*MAXN];int seq[2*MAXN];int seq1[2*MAXN];int depth[2*MAXN];int first[MAXN];int dp[2*MAXN][25];int parent[MAXN];int st[MAXN];int ed[MAXN];int cnt, cnt1;int Lowbit(int x){ return x & (-x);}void Add(int x, int val, int n){ if(x <= 0) return ; for(int i = x; i <= n; i += Lowbit(i)) { s[i] += val; }}void Add1(int x, int val, int n){ if(x <= 0) return ; for(int i = x; i <= n; i += Lowbit(i)) { s1[i] += val; }}int Sum(int x){ int res = 0; for(int i = x; i > 0; i -= Lowbit(i)) { res += s[i]; } return res;}int Sum1(int x){ int res = 0; for(int i = x; i > 0; i -= Lowbit(i)) { res += s1[i]; } return res;}void RMQ_Init(int n){ for(int i = 1; i <= n; i++) { dp[i][0] = i; } for(int j = 1; (1 << j) <= n; j++) { for(int i = 1; i + (1 << j) - 1 <= n; i++) { int a = dp[i][j-1], b = dp[i + (1 << (j-1))][j-1]; dp[i][j] = depth[a] < depth[b] ? a : b; } }}int RMQ_Query(int l, int r){ int k = 0; while((1 << (k + 1)) <= r - l + 1) k++; int a = dp[l][k], b = dp[r-(1 << k)+1][k]; return depth[a] < depth[b] ? a : b;}int LCA(int u, int v){ int a = first[u], b = first[v]; if(a > b) a ^= b, b ^= a, a ^= b; int res = RMQ_Query(a, b); return seq1[res];}void Dfs(int u, int fa, int dep){ seq[++cnt] = u; seq1[++cnt1] = u; first[u] = cnt1; parent[u] = fa; depth[cnt1] = dep; st[u] = cnt; int len = edge[u].size(); for(int i = 0; i < len; i++) { int v = edge[u][i]; if(v != fa) { Dfs(v, u, dep+1); seq1[++cnt1] = u; depth[cnt1] = dep; } } seq[++cnt] = u; ed[u] = cnt;}void Init(int n){ for(int i = 0; i <= n; i++) { edge[i].clear(); } memset(s, 0, sizeof(s)); memset(s1, 0, sizeof(s1));}void Debug(){ int u, v; while(1) { scanf("%d %d", &u, &v); printf("The LCA of %d %d is %d\n", u, v, LCA(u, v)); }}int main(){ int n, op; int u, v, w; int cmd; while(scanf("%d %d", &n, &op) != EOF) { Init(n); for(int i = 0; i < n-1; i++) { scanf("%d %d", &u, &v); edge[u].push_back(v); edge[v].push_back(u); } cnt = cnt1 = 0; Dfs(1, 0, 0); RMQ_Init(cnt1); while(op--) { scanf("%d", &cmd); if(cmd == 0) { scanf("%d %d", &u, &w); Add(st[u], w * (1 - depth[first[u]]), cnt); Add(ed[u], -w * (1 - depth[first[u]]), cnt); Add1(st[u], w, cnt); Add1(ed[u], -w, cnt); } else if(cmd == 1) { scanf("%d %d", &u, &v); int lca = LCA(u, v); int par = parent[lca]; int ans = Sum(st[u]); ans += depth[first[u]] * Sum1(st[u]); ans += Sum(st[v]); ans += depth[first[v]] * Sum1(st[v]); ans -= Sum(st[lca]); ans -= depth[first[lca]] * Sum1(st[lca]); ans -= Sum(st[par]); ans -= depth[first[par]] * Sum1(st[par]); printf("%d\n", ans); } } } return 0;}
- dfs序基本类型总结
- dfs序基本类型 详细
- Java基本类型总结
- 各种语言基本类型 总结
- Java基本类型转换总结
- Go 语言基本类型总结
- 基本类型、操作符总结
- Java基本类型用法总结
- Java的基本类型总结
- php中的基本类型总结
- 1-javascript基本类型总结
- Java数据类型总结:基本类型、引用类型
- Java基本类型和包装类型总结
- 基本类型和封装类型【总结】
- DFS序总结
- map类型的基本操作总结
- Java基础知识概括总结二 基本类型
- OpenCV 基本函数和类型总结
- 史上最全的java随机数生成算法(转)
- Oracle按季度统计语句
- 苹果尺寸
- mkdir -p命令
- filter简介
- dfs序基本类型总结
- 国际化的原理、步骤、分类
- The method of type must override a superclass method解决方式
- ONE模拟过程
- java spring 记录用户增删改操作日志
- android开源项目 Google code
- android 代码规范
- mongodb的安装、创建数据库、设置用户(Window 7)
- 黑马程序员——正则表达式