【线段树分治】(luoguP5787、p4585、cf938G)

洛谷P5787:https://www.luogu.com.cn/problem/P5787

题意:某条边 u v 会 在 l , r  时间段内存在,问每个时间点的图是不是二分图。

按照时间轴建树,首先对于修改操作,一个修改操作在线段树上操作会修改 logn 个节点的vector,最后再dfs遍历一整遍处理出询问。

关于判断二分图,其实并查集维护,判断是否有奇环即可。  dis[ u ] 记录 u 到 并查集 直接 父亲 的距离奇偶,然后并查集启发式合并,修改的只有fu的 父亲, fu 的 dis( 变成 dis[u] ^ dis[v] ^ 1), 以及 fv 的siz,Dis(u) 函数计算u到祖父亲的距离奇偶,计算的时候一直往上跑就好了。然后关于正确性:假如说u v 不在一个集合,那么把 fu 和 fv 并起来(也就是上文所说的操作),可以观察到计算Dis 值的时候是正确的。

假如说 u v 不在一个集合 , 那么u、v现在的并查集树绝对是树而没有环,假如 u v 之间有 一条非树边,构成的环一定是偶环,如果是奇环前面就会判断掉,那么u 不经过 非树边 和经过非树边到达 祖父亲的距离奇偶一定是一样的,所以不用保存非树边,也就是现在并查集是树。同样道理,u v 连非树边,假如与树边构成偶环,什么东西也不用变。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N = 3e5+9;
 4 int cnt = 0;
 5 vector< pair<int,int> > tr[N*4];
 6 pair<int,int > sta[N];
 7 int f[N],dis[N],siz[N],ans[N];
 8 int find(int x){ if(x == f[x]) return x; return find(f[x]);};
 9 int Dis(int x){
10     int res = 0;
11     while(x != f[x]){
12         res ^= dis[x];
13         x = f[x];
14     }
15     return res;
16 }
17 void merge(int u,int v){
18     int fu = find(u) , fv = find(v);
19     if(siz[fu] > siz[fv]){
20         swap(u,v);
21         swap(fu,fv);
22     }
23     f[ fu ] = fv;
24     dis[fu] = ( dis[u] ^ dis[v] ^ 1 );
25     siz[fv] += siz[fu];
26     sta[++cnt] = make_pair(fu,fv);
27 }
28 void add(int o,int l,int r,int x,int y,pair<int,int> w){
29     if( x<= l && r <= y){
30         tr[o].push_back(w);
31         return;
32     }
33     int m = (l+r)>>1;
34     if(x<=m) add(o<<1,l,m,x,y,w);
35     if( y > m) add(o<<1|1,m+1,r,x,y,w);
36     return;
37 }
38 void dfs(int o,int l,int r){
39     int k = cnt;
40     bool ok = 1;
41     for(auto w : tr[o] ){
42         int u = w.first,v = w.second;
43         int fu = find(u) , fv = find(v);
44         if( fu == fv ){
45             if( (Dis(u) ^ Dis(v) ) == 0 ){
46                 ok = 0;
47                 for(int i = l;i<=r;++i) ans[i] = 0;
48                 break;
49             }
50         }
51         else merge(u,v);
52     }
53     if(ok){
54         if(l==r) ans[l] = 1;
55         else{
56             int m = (l+r)>>1;
57             dfs(o<<1,l,m);
58             dfs(o<<1|1,m+1,r);
59         }
60     }
61     while(cnt != k){
62         int u = sta[cnt].first , v = sta[cnt].second;
63         f[u] = u;
64         dis[u] = 0;
65         siz[v] -= siz[u];
66         --cnt;
67     }
68 }
69 int main(){
70     int n,m,k;
71     scanf("%d %d %d",&n,&m,&k);
72     for(int i = 1;i<=m;++i){
73         int u,v,l,r; scanf("%d %d %d %d",&u,&v,&l,&r);
74         if( l < r ) add(1,1,k,l+1,r,make_pair(u,v));
75     }
76     for(int i = 1;i<=n;++i) f[i] = i , siz[i] = 1,dis[i] = 0;
77     dfs(1,1,k);
78     for(int i = 1;i<=n;++i){
79         if(ans[i]) puts("Yes");
80         else puts("No");
81     }
82     return 0;
83 }
View Code

 

bzoj4311

题意:二维平面删除插入某些点,询问当前平面内的点与询问点最大的点积是多少。

先不考虑插入删除,平面上的n个点,只有上凸包是答案,然后我们考虑用线段树分治维护上凸包。我们首先对插入点按照x 轴排序,方便接下来的维护凸包,然后对于每个点,找到它对应的线段树上logN个区间节点,把这个点插入到这log个区间节点上(每个节点维护这一个上凸包),然后对于询问。重点来了!!!线段树分治的询问操作不是一定一成不变的直接遍历线段树,在这题当中是不可行的,因为直接遍历,遍历非根节点的时候我们不知到询问斜率是什么,并且在回溯这个撤销操作上也不太方便。

其实我们可以直接暴力!!!!直接枚举询问的叶子节点是哪一个,然后从根节点直接递归到叶子节点,中途节点的凸包三分一下找到答案,最后对所有答案取max即可。但是这题主要是每个区间节点可以三分来求,所以复杂度nloglog,但是其他题就不好说了。

bzoj现在还交不了题,挖个坑,日后补。

 

洛谷p4585:https://www.luogu.com.cn/problem/P4585

题意:有n个商店,m个操作,每个操作:1、当天在某个商店进了价值为x的商品。2、查询最近d天以内进货的,商店编号在l,r之间的商品 与 x 值异或的最大值。

我们可以用线段树分治来处理时间这一维,也就是先把询问的时间区间放到线段树上。然后我们在线段树上面建立可持久化trie来解决商品编号问题。

具体怎么解决,我们可以还是类似线段树分治的样子,遍历整个线段树,遍历到这个线段树节点,我们就重新构建可持久 trie,然后解决该节点的查询,然后对于我这个节点添加的信息,对时间进行分治,分别给左儿子以及右儿子,然后 一直下去。

正确性:询问拆成某几个线段树节点,也就是询问时间在这个节点区间内的点,然后我遍历整棵线段树的时候,添加的节点进行分治,递归到当前节点的vector,就是属于当前节点的添加,故正确。

时间复杂度:由于我每个添加点最多只插入log次,所以我总共插入nlog个节点进入可持久trie,然后我只开一个trie,每次清空直接trie.cnt = 0,重新构造即可。

感觉这题才是体现线段树分治的精髓。

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 const int N = 1e5+9;
  4 struct Que{
  5     int dl,dr,nl,nr,x;
  6     int ans;
  7 };
  8 vector<Que> q; 
  9 struct Add{
 10     int id,v;
 11     int day;
 12     bool operator < (Add& b)const{
 13         return id < b.id;
 14     }
 15 };
 16 struct Trie{
 17     int cnt;
 18     int tr[N*30][2],sum[N*30];
 19     int insert(int las,int val){
 20         int tem,now;
 21         tem = now = ++cnt;
 22         for(int i = 23;i>=0;--i){
 23             tr[now][0] = tr[las][0];
 24             tr[now][1] = tr[las][1];
 25             sum[now] = sum[las] + 1;
 26             bool t = (1<<i) & val;
 27             las = tr[las][t];
 28             tr[now][t] = ++cnt;
 29             now = tr[now][t];
 30         }
 31         sum[now] = sum[las] + 1;
 32         return tem;
 33     }
 34     int query(int l,int r,int val){
 35         int tem = 0;
 36         for(int i = 23;i>=0;--i){
 37             bool t = (1<<i) & val;
 38             if( sum[ tr[r][t^1] ] - sum[ tr[l][t^1] ] ){
 39                 tem |= (1<<i);
 40                 r = tr[r][t^1];
 41                 l = tr[l][t^1];
 42             }
 43             else r = tr[r][t] , l = tr[l][t];
 44         }
 45         return tem;
 46     }
 47 }trie;
 48 vector<int> tr[N*4];
 49 int root[N];
 50 void update(int o,int l,int r,int x,int y,int id){
 51     // cerr<<l<<" "<<r<<" "<<x<<" "<<y<<endl;
 52     if(x>y) return;
 53     if( x<=l && r<=y){
 54         tr[o].push_back(id);
 55         // cerr<<o<<" "<<l<<" "<<r<<" "<<id<<" id"<<endl;
 56         return;
 57     }
 58     int m = (l+r)>>1;
 59     if(x<=m) update(o<<1,l,m,x,y,id);
 60     if(y>m) update(o<<1|1,m+1,r,x,y,id);
 61 }
 62 void calc(int o,vector<Add>& add){
 63     trie.cnt = 0;
 64     vector<int> ins;
 65     int n = 0;
 66     for(auto it : add){
 67         ins.push_back(it.id);
 68         ++n;
 69         root[n] = trie.insert(root[n-1],it.v);
 70     }
 71     for(auto it : tr[o]){
 72         // cerr<<o<<" "<<it<<" oit"<<endl;
 73         int l = q[it].nl , r = q[it].nr;
 74         // cerr<<l<<" "<<r<<endl;
 75         l = lower_bound(ins.begin(),ins.end(),l) - ins.begin() + 1;
 76         r = upper_bound(ins.begin(),ins.end(),r) - ins.begin();
 77         // cerr<<o<<" "<<it<<" oit"<<l<<" "<<r<<endl;
 78         q[it].ans = max(q[it].ans,trie.query(root[l-1],root[r],q[it].x));
 79     }
 80 }
 81 void dfs(int o,int l,int r,vector<Add>& add){
 82     if(add.size() == 0 ) return;
 83     // cerr<<o<<" o"<<endl;
 84     calc(o,add);
 85     if(l==r) return;
 86     int m = (l+r)>>1;
 87     vector<Add> addl,addr;
 88     for(auto it : add){
 89         // cerr<<o<<" o"<<it.id<<" "<<it.v<<endl;
 90         if(it.day <= m) addl.push_back(it);
 91         else addr.push_back(it);
 92     }
 93     dfs(o<<1,l,m,addl);
 94     dfs(o<<1|1,m+1,r,addr);
 95 }
 96 int main(){
 97     int n,m; scanf("%d %d",&n,&m);
 98     for(int i = 1;i<=n;++i){
 99         int a; scanf("%d",&a);
100         root[i] = trie.insert(root[i-1],a);
101     }
102     vector<Add> add;
103     for(int i = 1;i<=m;++i){
104         int ty; scanf("%d",&ty);
105         if(ty == 0){
106             int s,v; scanf("%d %d",&s,&v);
107             int cnt = add.size();
108             add.push_back((Add){s,v,cnt+1});
109         }
110         else{
111             int l,r,x,d; scanf("%d %d %d %d",&l,&r,&x,&d);
112             int ans = trie.query(root[l-1],root[r],x);
113             q.push_back((Que){max(1,(int)add.size()-d+1),(int)add.size(),l,r,x,ans});
114         }
115     }
116     // for(auto it : q) cerr<<it.dl<<" "<<it.dr<<" day"<<endl;
117     for(int i = 0;i<q.size();++i) update(1,1,(int)add.size(),q[i].dl,q[i].dr,i);
118     sort(add.begin(),add.end());
119     dfs(1,1,add.size(),add);
120     for(auto it : q) printf("%d\n",it.ans);
121     return 0;
122 }
View Code

 

codeforces 938G:https://codeforces.com/contest/938/problem/G

题意:给你一张带边权无向图,m个操作,每个操作加边,删边,询问两个点之间的最短异或路径。

考虑线段树分治,对于加减边,边的存活肯定是某一段时间,把时间区间把它放到线段树上,就相当于某几个线段树节点要加某些边。 然后对于询问,单点询问,线段树分治的单点询问其实有两种做法,一种做法是先把询问遍历到 线段树叶子节点,然后加tag,另一种做法就是在遍历的时候维护一个询问的vector,然后边遍历边分治就好了。

这题询问路径异或,线性基可以处理一张图的两点的异或最大最小,其实就是把环丢进线性基。然后每一次回溯的时候用可撤销并查集维护就好了。就是线性基维护非树边,并查集维护树边。然后线性基撤销?不存在的,每个线段树节点开一个线性基好了。hhh

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 typedef long long ll;
  4 const int MN = 29;
  5 const int N = 2e5+9;
  6 struct Base{
  7     int a[32];
  8     Base(){memset(a,0,sizeof(a));}
  9     void ins(int x){
 10         for(int i = MN;i>=0;--i){
 11             if( x & (1<<i) ){
 12                 if(a[i]) x ^= a[i];
 13                 else{
 14                     a[i] = x;
 15                     break;
 16                 }
 17             }
 18         }
 19     }
 20     int qmin(int x){
 21         int res = x;
 22         for(int i = MN;i>=0;--i) res = min(res,res ^ a[i]);
 23         return res;
 24     }
 25 }base;
 26 struct Edge{
 27     int u,v,w;
 28 };
 29 struct Node{
 30     Base bas;
 31     vector<Edge> add;
 32 }tr[N<<2];
 33 struct Query{
 34     int u,v,id,tim;
 35 };
 36 vector<Query> que;
 37 int ans[N];
 38 int dis[N],f[N],siz[N],top = 0;
 39 pair<int,int> sta[N];
 40 map< pair<int,int> , pair<int,int> > mp;
 41 void add_edge(int o,int l,int r,int x,int y,Edge e){
 42     if(x<=l && r <= y){
 43         tr[o].add.push_back(e);
 44         return;
 45     }
 46     int m = (l+r)>>1;
 47     if(x<=m) add_edge(o<<1,l,m,x,y,e);
 48     if(y>m) add_edge(o<<1|1,m+1,r,x,y,e);
 49 }
 50 int find(int x){
 51     if( x == f[x]) return x;
 52     return find(f[x]);
 53 }
 54 int find_dis(int x){
 55     int res = 0;
 56     while( x != f[x] ){
 57         res ^= dis[x];
 58         x = f[x];
 59     }
 60     return res;
 61 }
 62 void merge(int u,int fu,int v,int fv,int w){
 63     if(siz[fu] > siz[fv] ){
 64         swap(u,v);
 65         swap(fu,fv);
 66     }
 67     siz[fv] += siz[fu];
 68     f[fu] = fv;
 69     dis[fu] = find_dis(u) ^ w ^ find_dis(v);
 70     sta[++top] = make_pair(fu,fv);
 71 }
 72 void work(int o,vector<Query> q){
 73     for(auto it : tr[o].add){
 74         int u = it.u , v = it.v , w = it.w;
 75         int fu = find(u) , fv = find(v);
 76         if(fu == fv ) tr[o].bas.ins( find_dis(u) ^ find_dis(v) ^ w);
 77         else merge(u,fu,v,fv,w);
 78     }
 79 }
 80 void roll_back(int tag){
 81     while(top != tag){
 82         int fu = sta[top].first , fv = sta[top].second;
 83         --top;
 84         siz[fv] -= siz[fu];
 85         f[fu] = fu;
 86         dis[fu] = 0;
 87     }
 88 }
 89 void dfs(int o,int l,int r,vector<Query> q){
 90     int tag = top;
 91     work(o,q);
 92     if(l==r){
 93         for(auto it : q){
 94             int u = it.u, v = it.v,id=  it.id;
 95             int fu = find(u) , fv = find(v);
 96             if(fu != fv) continue;
 97             int tem = find_dis(u) ^ find_dis(v);
 98             ans[id] = min(ans[id],tr[o].bas.qmin(tem));
 99         }   
100         roll_back(tag);
101         return;
102     }
103     tr[o<<1].bas = tr[o].bas;
104     tr[o<<1|1].bas = tr[o].bas;
105     int m = (l+r)>>1;
106     vector<Query> ql,qr;
107     for(auto it : q){
108         if(it.tim <= m ) ql.push_back(it);
109         else qr.push_back(it);
110     }
111     dfs(o<<1,l,m,ql);
112     dfs(o<<1|1,m+1,r,qr);
113     roll_back(tag);
114 }
115 int main(){
116     int n,m; scanf("%d %d",&n,&m);
117     for(int i = 1;i<=n;++i) f[i] = i,siz[i] = 1;
118     memset(ans,127,sizeof(ans));
119     for(int i = 1;i<=m;++i){
120         int u,v,w; scanf("%d %d %d",&u,&v,&w);
121         if( u > v) swap(u,v);
122         mp[ make_pair(u,v) ] = make_pair(1,w);
123     }
124     int qn; scanf("%d",&qn);
125     int cnt = 0;
126     for(int i = 1;i<=qn;++i){
127         int ty,x,y,d; scanf("%d %d %d",&ty,&x,&y);
128         if( x > y) swap(x,y);
129         if(ty == 1){
130             scanf("%d",&d);
131             mp[ make_pair(x,y) ] = make_pair(i,d);
132         }
133         else if(ty == 2){
134             int las = mp[ make_pair(x,y)  ].first;
135             int w = mp[ make_pair(x,y) ].second;
136             add_edge(1,1,qn,las,i,(Edge){x,y,w});
137             mp.erase( make_pair(x,y) );
138         }
139         else{
140             Query tem = (Query){x,y,++cnt,i};
141             que.push_back(tem);
142         }
143     }
144     for(auto it : mp){
145         int u = it.first.first , v = it.first.second;
146         int w = it.second.second;
147         int tim = it.second.first;
148         add_edge(1,1,qn,tim,qn,(Edge){u,v,w});
149     }
150     dfs(1,1,qn,que);
151     // cerr<<cnt<<" !"<<endl;
152     for(int i = 1;i<=cnt;++i) printf("%d\n",ans[i]);
153     return 0;
154 }
View Code

 

posted @ 2020-04-03 17:17  小布鞋  阅读(187)  评论(0编辑  收藏  举报