codeforces 343D Water Tree 树链剖分 dfs序 线段树 set

题目链接

  这道题主要是要考虑到同一棵子树中dfs序是连续的

  然后我就直接上树剖了。。。

  

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 const int MAXN=600005;
  4
  5 struct Node
  6 {
  7     int l,r;
  8     int value;
  9     void init()
 10     {
 11         l=r=value=0;
 12     }
 13 }tree[4*MAXN];
 14 vector<int>nei[MAXN];
 15 int cnt=1;
 16 int top[MAXN],newarr[MAXN],newid[MAXN],siz[MAXN],hardchi[MAXN],par[MAXN];
 17 inline void dfs(const int &v,const int &pa)
 18 {
 19     siz[v]=1;par[v]=pa;
 20     int masiz=0;
 21     for(int i=0;i<nei[v].size();i++)
 22     {
 23         int u=nei[v][i];
 24         if(u==pa) continue;
 25         dfs(u,v);
 26         siz[v]+=siz[u];
 27         if(siz[u]>masiz)
 28         {
 29             masiz=siz[u];
 30             hardchi[v]=u;
 31         }
 32     }
 33 }
 34 inline void findHardPath(const int &v,const int &Top)
 35 {
 36     top[v]=Top;
 37     newarr[cnt]=v;
 38     newid[v]=cnt++;
 39     if(hardchi[v]!=-1)findHardPath(hardchi[v],Top);
 40     for(int i=0;i<nei[v].size();i++)
 41     {
 42         int u=nei[v][i];
 43         if(u==par[v]||u==hardchi[v])continue; findHardPath(u,u);
 44     }
 45 }
 46 void built(const int &i,const int &l,const int &r)
 47 {
 48     tree[i].l=l;tree[i].r=r;
 49     if(l==r) return;
 50     else
 51     {
 52         int mid=(l+r)>>1;
 53         built(i<<1,l,mid);
 54         built(i<<1|1,mid+1,r);
 55     }
 56 }
 57 inline void pushdown(int i)
 58 {
 59     if(tree[i].value==-1) return;
 60     tree[i<<1].value=tree[i].value;
 61     tree[i<<1|1].value=tree[i].value;
 62 }
 63 void change(const int &i,const int &l,const int &r,const int &w)
 64 {
 65     if(tree[i].l>=l&&tree[i].r<=r)
 66     {
 67         tree[i].value=w;
 68         pushdown(i);
 69         return;
 70     }
 71     else
 72     {
 73         pushdown(i);
 74         int mid=(tree[i].l+tree[i].r)>>1;
 75         if(l<=mid)change(i<<1,l,r,w);
 76         if(r>mid)change(i<<1|1,l,r,w);
 77         if(tree[i<<1].value==tree[i<<1|1].value) tree[i].value=tree[i<<1].value;
 78         else tree[i].value=-1;
 79     }
 80 }
 81 int res=-1;
 82 inline void query(const int &i,const int &x)
 83 {
 84     if(tree[i].value!=-1)
 85     {
 86         res=tree[i].value;
 87         return;
 88     }
 89     int mid=(tree[i].l+tree[i].r)>>1;
 90     if(x<=mid)query(i<<1,x);
 91     else query(i<<1|1,x);
 92 }
 93 int main()
 94 {
 95     int n;
 96     scanf("%d",&n);
 97     for(int i=0;i<n-1;i++)
 98     {
 99         int x,y;
100         scanf("%d %d",&x,&y);
101         nei[x].push_back(y);
102         nei[y].push_back(x);
103     }
104     memset(hardchi,-1,sizeof hardchi);
105     dfs(1,1);
106     findHardPath(1,1);
107     built(1,1,n);
108     int m;
109     scanf("%d",&m);
110     for(int i=0;i<m;i++)
111     {
112         int x,y;
113         scanf("%d %d",&x,&y);
114         if(x==1) change(1,newid[y],newid[y]+siz[y]-1,1);
115         else if(x==2)
116         {
117             while(top[y]!=1)
118             {
119                 change(1,newid[top[y]],newid[y],0);
120                 y=par[top[y]];
121             }
122             change(1,newid[1],newid[y],0);
123         }
124         else
125         {
126             query(1,newid[y]);
127             printf("%d\n",res);
128         }
129     }
130     return 0;
131 }

  其实完全不用树链剖分,我们可以重新考虑一下题目中的3种操作

  1、将子树全部赋成1,直接dfs序+线段树区间修改

  2、将节点的祖先全部赋成0。我们可以知道,如果一个节点的子树中有1个0,那么这个节点一定会被赋成0,所以只要线段树单点修改就可以了。

  3、查询。查一下是该节点的子树有没有0,该节点有没有被赋成1,如果都有就再比较一下操作的先后

 1 #include <stdio.h>
 2 #include <vector>
 3 #include <memory.h>
 4 #include <algorithm>
 5 using namespace std;
 6 #define MN 3000005
 7 const int oo = 1000000007;
 8 int n, m;
 9 int top, L[MN], R[MN], l[MN], h[MN], r[MN], d[MN], leaf[MN];
10 bool pos[MN];
11 vector <int> G[MN];
12 void Make(int r, int lo, int hi) {
13     l[r] = lo; h[r] = hi;
14     if (lo == hi) { leaf[lo] = r; return; }
15     Make(r<<1, lo, (lo+hi)>>1);
16     Make((r<<1)+1, (lo+hi)/2+1, hi);
17 }
18 void go(int u) {
19     top++; L[u]= top; pos[u] = 0;
20     int sz = G[u].size();
21     for (int i = 0; i < sz; i++) if (pos[G[u][i]]) go(G[u][i]);
22     R[u] = top;
23 }
24 void upd(int p, int lo, int hi, int y) {
25     if (l[p] > hi || h[p] < lo) return;
26     if (l[p] >= lo && h[p] <= hi) { r[p] = y; return; }
27     upd(p<<1, lo, hi, y);
28     upd((p<<1)+1, lo, hi, y);
29 }
30 void del(int v, int w) {
31     int p = leaf[v];
32     while (p) { d[p] = w; p >>= 1; }
33 }
34 int F(int u) {
35     int p = leaf[u], ret = 0;
36     while (p) { if (ret < r[p]) ret = r[p]; p >>= 1; }
37     return ret;
38 }
39 inline int fmax(int a, int b) { if (a > b) return a; return b; }
40 int T(int r, int lo, int hi) {
41     if (l[r] > hi || h[r] < lo) return 0;
42     if (l[r] >= lo && h[r] <= hi) return d[r];
43     return fmax(T(r<<1,lo,hi), T((r<<1)+1,lo,hi));
44 }
45 int main() {
46     int u, v, K, s, f;
47     scanf("%d",&n);
48     for (int i = 1; i <= n-1; i++) {
49         scanf("%d%d",&u,&v);
50         G[u].push_back(v);
51         G[v].push_back(u);
52     }
53     memset(pos, 1, sizeof(pos));
54     go(1);
55     Make(1, 1, n+10);
56     scanf("%d",&m);
57     for (int i = 1; i <= m; i++) {
58         scanf("%d%d",&K,&v);
59         s = L[v]; f = R[v];
60         if (K == 1) upd(1, s, f, i);
61         if (K == 2) del(s, i);
62         if (K == 3) {
63             if (F(s) > T(1, s, f)) printf("1\n");
64             else printf("0\n");
65         }
66     }
67     return 0;
68 }

  其实还可以更简单!

  连线段树都不用,直接用set就好了!!

 1 #include <bits/stdc++.h>
 2 #define N 500005
 3 using namespace std;
 4 int tid;
 5 vector<int> a[N];
 6 int st[N * 2], ed[N * 2], parent[N * 2];
 7 void dfs(int x, int p = 0){
 8   st[x] = ++tid;
 9   parent[x] = p;
10   for (int i = 0; i < a[x].size(); ++ i){
11     int y = a[x][i];
12     if (y == p)  continue;
13     dfs(y, x);
14   }
15   ed[x] = tid;
16 }
17 int main() {
18   int n, q, x, y, c, v;
19   scanf("%d", &n);
20   for (int i = 0; i < n-1; i++) {
21     scanf("%d%d", &x, &y);
22     a[x].push_back(y);
23     a[y].push_back(x);
24   }
25   dfs(1);
26   scanf("%d", &q);
27   set<int> Empty;
28   for (int i = 1; i <= n; i++) Empty.insert(st[i]);
29   Empty.insert(tid+1);
30   for (int i = 1; i <= q; i++) {
31     scanf("%d%d", &c, &v);
32     if (c == 1) {
33       if (*(Empty.lower_bound(st[v])) <= ed[v]) { // it is empty right now
34         if (v > 1) Empty.insert(st[parent[v]]);
35         Empty.erase(Empty.lower_bound(st[v]), Empty.upper_bound(ed[v]));
36       }
37     }
38     if (c == 2) Empty.insert(st[v]);
39     if (c == 3) puts(*(Empty.lower_bound(st[v])) <= ed[v] ? "0" : "1");
40   }
41   return 0;
42 }

原文地址:https://www.cnblogs.com/unnamed05/p/9304276.html

时间: 2024-12-18 00:01:17

codeforces 343D Water Tree 树链剖分 dfs序 线段树 set的相关文章

BZOJ 2819 Nim 树链剖分/DFS序+LCA+树状数组

题意:给定一棵树,每个节点是一堆石子,给定两种操作: 1.改变x号节点的石子数量 2.用从x到y的路径上的所有堆石子玩一次Nim游戏,询问是否有必胜策略 Nim游戏有必胜策略的充要条件是所有堆的石子数异或起来不为零 这题首先一看就是树链剖分 然后题目很善良地告诉我们深搜会爆栈 于是我们可以选择广搜版的树链剖分 BFS序从左到右是深搜,从右到左是回溯,一遍BFS就够 单点修改区间查询还可以套用ZKW线段树 不过这题其实不用这么麻烦 有更简单的办法 详见 http://dzy493941464.is

BZOJ 2286 树链剖分+DFS序+虚树+树形DP

第一次学习虚树,就是把无关的点去掉.S里维护一条链即可. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <algorithm> 5 #define LL long long 6 using namespace std; 7 const LL Maxm=501000; 8 const LL Maxn=250100; 9 const LL Inf=1e60; 1

BZOJ 2243:染色(树链剖分+区间合并线段树)

[SDOI2011]染色Description给定一棵有n个节点的无根树和m个操作,操作有2类:1.将节点a到节点b路径上所有点都染成颜色c:2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”.请你写一个程序依次完成这m个操作.Input第一行包含2个整数n和m,分别表示节点数和操作数:第二行包含n个正整数表示n个节点的初始颜色下面 行每行包含两个整数x和y,表示x和y之间有一条无向边.下面 行每行描述一个操作:“C

【BZOJ4515】游戏,树链剖分+永久化标记线段树维护线段信息(李超线段树)

Time:2016.05.10 Author:xiaoyimi 转载注明出处谢谢 传送门 思路: 李超线段树 一开始听faebdc讲,并没有听的很懂ww 后来找到良心博文啊有木有 折越 首先可以把修改转换一下,因为那个dis非常不爽.显然s~t的路径有s~lca和lca~t组成.令d[x]表示x的深度,对于s~lca上面的点,修改的值相当于a*(d[s]-d[x])+b=-a*d[x]+(b-a*d[s]),lca~t上面的点的值相当于a*(d[s]+d[x]-2*d[lca])+b=a*d[x

BZOJ 2243: [SDOI2011]染色 树链剖分 倍增lca 线段树

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=2243 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写

【XSY2667】摧毁图状树 贪心 堆 DFS序 线段树

题目大意 给你一棵有根树,有\(n\)个点.还有一个参数\(k\).你每次要删除一条长度为\(k\)(\(k\)个点)的祖先-后代链,问你最少几次删完.现在有\(q\)个询问,每次给你一个\(k\),问你答案是多少. \(n\leq {10}^5,k\leq {10}^9\) 题解 设\(l\)为这棵树的叶子个数,显然当\(k>\)树的深度时答案都是\(l\). 下面要证明:答案是\(O(l+\frac{n-l}{k})\)的. 我们从下往上贪心,每次选择一个未被覆盖的深度最深的点,覆盖这个点网

【树链剖分】【线段树】bzoj3626 [LNOI2014]LCA

引用题解: http://blog.csdn.net/popoqqq/article/details/38823457 题目大意: 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)].(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和) 这题看见了直接卡壳...然后

【块状树】【树链剖分】【线段树】bzoj3531 [Sdoi2014]旅行

离线后以宗教为第一关键字,操作时间为第二关键字排序. <法一>块状树,线下ac,线上tle…… #include<cstdio> #include<cmath> #include<algorithm> #include<cstring> #include<queue> using namespace std; queue<int>q; int f,c; inline void R(int &x){ c=0;f=1;

[GDOI2016] 疯狂动物园 [树链剖分+可持久化线段树]

题面 太长了,而且解释的不清楚,我来给个简化版的题意: 给定一棵$n$个点的数,每个点有点权,你需要实现以下$m$个操作 操作1,把$x$到$y$的路径上的所有点的权值都加上$delta$,并且更新一个版本 操作2,对于有向路径$(x,y)$上的点$a_i$,求下面的和值: $\sum_{i=1}^{len} a_i \sum_{j=1}^{len-i} j$ 操作3,回到第$i$个版本(但是下一次更新以后还是到总版本号+1的那个版本) 思路 这个题显然一眼就是树剖+可持久化数据结构啊 那么核心