AcWing提高算法课Level-3 第四章 高级数据结构

并查集
AcWing 1250. 格子游戏1167人打卡
AcWing 1252. 搭配购买1064人打卡
AcWing 237. 程序自动分析940人打卡
AcWing 239. 奇偶游戏629人打卡
AcWing 238. 银河英雄传说844人打卡

树状数组
AcWing 241. 楼兰图腾1083人打卡
AcWing 242. 一个简单的整数问题1050人打卡
AcWing 243. 一个简单的整数问题2916人打卡
AcWing 244. 谜一样的牛869人打卡

线段树
AcWing 1275. 最大数1125人打卡
AcWing 245. 你能回答这些问题吗955人打卡
AcWing 246. 区间最大公约数762人打卡
AcWing 243. 一个简单的整数问题2845人打卡
AcWing 247. 亚特兰蒂斯482人打卡
AcWing 1277. 维护序列638人打卡

可持久化数据结构
AcWing 256. 最大异或和388人打卡
AcWing 255. 第K小数428人打卡

平衡树
AcWing 253. 普通平衡树415人打卡
AcWing 265. 营业额统计344人打卡

AC自动机
AcWing 1282. 搜索关键词441人打卡
AcWing 1285. 单词297人打卡

代码

AcWing 1250. 格子游戏1167人打卡

//题意:给出n*n的网格,按顺序m次操作,每次从(x,y)向右或下连画一条边,求第几次时能围成封闭的图形。
//思路:把n*n个坐标看成对应的点(可以用x*n+y映射),问题转换为每次连一条边,求最早何时形成环。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 210;int fa[maxn*maxn+10];
void init(int n){for(int i = 1; i <= n; i++)fa[i]=i;}
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void merge(int x, int y){x=find(x);y=find(y);if(x!=y)fa[x]=y;}int getid(int x, int y){return x*maxn+y;
}int main(){ios::sync_with_stdio(false);int n, m;   cin>>n>>m;init(maxn*maxn);for(int i = 1; i <= m; i++){int x, y;  char op;cin>>x>>y>>op;int a = getid(x,y), b = getid(x+(op=='D'), y+(op=='R'));int aa=find(a), bb=find(b);if(aa==bb){cout<<i<<"\n";return 0;}else{merge(aa,bb);}}cout<<"draw\n";return 0;
}

AcWing 1252. 搭配购买1064人打卡

//题意:n朵云,每朵有价钱和价值。m个关系,买了u就必须买v。一共有w钱,最大化价值。
//思路:m个关系并查集处理后,得到若干个独立的连通块,把这些块作为物品,计算出价钱和价值,跑01背包即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 10010;int c[maxn], d[maxn];
int dp[maxn];int fa[maxn+10];
void init(int n){for(int i = 1; i <= n; i++)fa[i]=i;}
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void merge(int x, int y){x=find(x);y=find(y);if(x!=y)fa[x]=y;}int main(){ios::sync_with_stdio(false);//UnionFindSetint n, m, w;  cin>>n>>m>>w;for(int i = 1; i <= n; i++)cin>>c[i]>>d[i];//价钱,价值init(n);for(int i = 1; i <= m; i++){int u, v;  cin>>u>>v;  merge(u,v);}map<int,int>cc, dd;for(int i = 1; i <= n; i++){cc[find(i)] += c[i];dd[find(i)] += d[i];}vector<pair<int,int> >vc(cc.begin(),cc.end()), vd(dd.begin(),dd.end());//01 Packfor(int i = 0; i < vc.size(); i++){for(int j = w; j >= vc[i].second; j--){dp[j] = max(dp[j], dp[j-vc[i].second]+vd[i].second);}}cout<<dp[w]<<"\n";return 0;
}

AcWing 237. 程序自动分析940人打卡

//题意:n个条件,每个条件(xi,xj,e)表示xi==xj或xi!=xj,判断是否有冲突。
//思路:先并查集合并所有xi==xj的数到相同集合,然后跑xi!=xj的判断冲突。因为数字比较大所以先离散化一下。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;vector<pair<int,int> >eq, uneq;//离散化
unordered_map<int,int>ma; int cnt = 0;
int mp(int x){if(!ma.count(x))ma[x]=++cnt;return ma[x];
}//并查集
int fa[maxn+10];
void init(int n){ for(int i = 1; i <= n; i++)fa[i]=i; }
int find(int x){ return x==fa[x]?x:fa[x]=find(fa[x]); }
void merge(int x, int y){x=find(x);y=find(y);if(x!=y)fa[x]=y;}int main(){ios::sync_with_stdio(false);int T;  cin>>T;while(T--){eq.clear(), uneq.clear();cnt = 0;  ma.clear();int n;  cin>>n;for(int i = 1; i <= n; i++){int x, y, e;  cin>>x>>y>>e;x = mp(x), y = mp(y);if(e==1){eq.push_back({x,y});}else{uneq.push_back({x,y});}}init(cnt);for(auto x : eq)merge(x.first, x.second);int ok = 1;for(auto x : uneq){if(find(x.first)==find(x.second)){ok = 0; break;}}if(ok==0)cout<<"NO\n";else cout<<"YES\n";}return 0;
}

AcWing 239. 奇偶游戏629人打卡

//题意:有一个长为n的01序列(未给出),按顺序给出m次询问的答案,每次回答区间[l,r]中1的个数是奇数还是偶数,求从第几个询问开始冲突。
/*思路:
+ 用s[i]表示原序列的前缀和,那么就可以得到区间[l,r]中1的个数(即s[r]-s[l-1])的奇偶性。
+ 当s[r]-s[l-1]为奇数时,可以推出,s[r]与s[l-1]肯定是一个奇数一个偶数。反之两个肯定都为奇数或者偶数。
+ 用扩展域维护关系的传递性,将每个节点x拆为奇数域与偶数域,分别表示x是奇数和x是偶数,维护合并的时候判断冲突信息即可。
+ 节点范围1e9较大,需要离散化一下。
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;//离散化
unordered_map<int,int>ma; int cnt = 0;
int mp(int x){if(!ma.count(x))ma[x]=++cnt;return ma[x];
}//并查集
int fa[2*maxn+10];
void init(int n){ for(int i = 1; i <= n; i++)fa[i]=i; }
int find(int x){ return x==fa[x]?x:fa[x]=find(fa[x]); }
void merge(int x, int y){x=find(x);y=find(y);if(x!=y)fa[x]=y;}int main(){ios::sync_with_stdio(false);int n;  cin>>n;int m;  cin>>m;init(2*maxn);int ans = m; //无矛盾时答案即为问题的数量for(int i = 1; i <= m; i++){int l, r; string op;  cin>>l>>r>>op;l = mp(l-1), r = mp(r);   //sum[l~r]=s[r]-s[l-1];if(op=="even"){           //sum[l~r]为偶数,即s[l-1]和s[r]奇偶性相同if(find(l+maxn) == find(r)){ //冲突了ans = i-1; break;}merge(l,r);          //s[l]和s[r]都是奇数merge(l+maxn,r+maxn);//或者s[l]和s[r]都是偶数}else{if(find(l)==find(r)){ans = i-1; break;}merge(l+maxn, r);merge(l,r+maxn);}}cout<<ans<<"\n";return 0;
}

AcWing 238. 银河英雄传说844人打卡

//带权并查集
//维护每个节点(战舰)到该列舰首的位置和每列战舰的长度
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e5+10;
typedef long long LL;int fa[maxn+10];
int sz[maxn], rk[maxn];
void init(int n){for(int i = 1; i <= n; i++)fa[i]=i,sz[i]=1;}
int find(int x){if(fa[x]==x)return x;else{int t = find(fa[x]);rk[x] += rk[fa[x]];return fa[x]=t;}
}
void merge(int x, int y){x=find(x);y=find(y);if(x!=y){fa[x]=y;rk[x] += sz[y];sz[y] += sz[x];sz[x] = 0;}
}int main(){ios::sync_with_stdio(false);int n = 30000;init(n);int T;  cin>>T;while(T--){char op; int a, b;cin>>op>>a>>b;if(op=='M'){merge(a,b);//a整列接到b后面}else{if(find(a)!=find(b)){cout<<"-1\n";}else{cout<<abs(rk[a]-rk[b])-1<<"\n";}}}return 0;
}

AcWing 241. 楼兰图腾1083人打卡

//题意:给出一条横轴上的n个点的纵坐标,求有多少个v和∧图形(中间点y值高于两边或低于两边)
//思路:从左向右依次遍历每个数a[i],使用树状数组统计在i位置之前所有比a[i]大的数的个数、以及比a[i]小的数的个数。统计完成后,将a[i]加入到树状数组。
//思路:再从右向左依次遍历每个数a[i],使用树状数组统计在i位置之后所有比a[i]大的数的个数、以及比a[i]小的数的个数。统计完成后,将a[i]加入到树状数组。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2000010;int a[maxn];
int sm[maxn], bg[maxn];//左边比a[i]小,比a[i]大的数的个数//树状数组
int n, b[maxn];
void add(int x, int v){ for(int i = x; i <= n; i+=i&(-i))b[i]+=v;}//序列中第x个数加上k
int query(int x){ int ans=0;for(int i=x;i>0;i-=i&(-i))ans+=b[i];return ans;}//查询序列前x个数的和int main(){ios::sync_with_stdio(false);cin>>n;for(int i = 1; i <= n; i++)cin>>a[i];for(int i = 1; i <= n; i++){//从左往右,暂存数据int y = a[i];sm[i] = query(y-1); //位置i前的数中,数字[1,y-1]的出现次数bg[i] = (i-1)-query(y);//总个数-小于等于a[i]的数的个数add(y,1);}memset(b,0,sizeof(b));LL ansv = 0, ansa = 0;for(int i = n; i >= 1; i--){//从右往左,更新答案int y = a[i];ansa += (LL)sm[i]*(query(y-1)); //右边比a[i]小的ansv += (LL)bg[i]*(query(n)-query(y));//右边比a[i]大的add(y,1);}cout<<ansv<<" "<<ansa<<"\n";return 0;
}

AcWing 242. 一个简单的整数问题1050人打卡

//题意:维护一个序列,支持区间加值和单点询问
//思路:树状数组维护差分序列,查询操作等价于对差分做前缀和,即单点的值。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2000010;//树状数组
int n, b[maxn];
void add(int x, int v){ for(int i = x; i <= n; i+=i&(-i))b[i]+=v;}//序列中第x个数加上k
int query(int x){ int ans=0;for(int i=x;i>0;i-=i&(-i))ans+=b[i];return ans;}//查询序列前x个数的和int main(){ios::sync_with_stdio(false);int q;  cin>>n>>q;for(int i = 1; i <= n; i++){int x;  cin>>x;add(i,x);  add(i+1,-x);//维护差分}while(q--){string op;  cin>>op;if(op=="Q"){int x;  cin>>x;cout<<query(x)<<"\n";//跑前缀和==单点查询}else{int l, r, x;  cin>>l>>r>>x;add(l,x); add(r+1,-x);//维护差分}}return 0;
}

AcWing 243. 一个简单的整数问题2916人打卡

//题意:维护一个序列,支持区间加值和区间询问
//思路:考虑上一题的区间加值,方法是维护差分数列,查询时sum{b1+b2+..bx}即为a[x]的值。
//思路:此时如果继续暴力区间查询的话,就是再套一层循环算出a1~ax的值,加起来时可以发现,对于[1,r]的查询,原式的值为ans=(r-1+1)b1+(r-2+1)b2+...+(r-r+1)br,即可以化简为(r-i+1)*bi,可以提取i*bi,单独再开一个树状数组维护一下。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5+10;//树状数组, 维护差分b[i]
LL n, b[maxn];
void add(int x, LL v){ for(int i = x; i <= n; i+=i&(-i))b[i]+=v;}//序列中第x个数加上k
LL query(int x){ LL ans=0;for(int i=x;i>0;i-=i&(-i))ans+=b[i];return ans;}//查询序列前x个数的和
//树状数组, 维护i*b[i]
LL b2[maxn];
void add2(int x, LL v){ for(int i = x; i <= n; i+=i&(-i))b2[i]+=v;}//序列中第x个数加上k
LL query2(int x){ LL ans=0;for(int i=x;i>0;i-=i&(-i))ans+=b2[i];return ans;}//查询序列前x个数的和//题目
LL a[maxn];
LL calc(int x){ return query(x)*(x+1)-query2(x); }int main(){ios::sync_with_stdio(false);int q;  cin>>n>>q;for(int i = 1; i <= n; i++){cin>>a[i];LL x = a[i]-a[i-1]; //插入原始的差分值add(i,x);  add2(i,x*i); }while(q--){string op;  cin>>op;if(op=="Q"){int l, r;  cin>>l>>r;cout<<calc(r)-calc(l-1)<<"\n";}else{int l, r, x;  cin>>l>>r>>x;add(l,x); add(r+1,-x);  //维护差分进行区间修改add2(l,l*x); add2(r+1,-(r+1)*x);}}return 0;
}

AcWing 244. 谜一样的牛869人打卡

//题意:1-n的数字随机排列,给出每个数字前比它小的数字个数,求原先的排列。
//思路:考虑最后一个数字,如果前面有x个比他小,那么他就是x+1。然后对于倒数第i个数字,就是那些已经知道数值的数字后,第n-1小的数+1。
//思路:新建全为1的数列代表所有位置的高度都未知,用树状数组维护前缀和,如果某位置前缀和==a[i]+1(即前面到此位置有a[i]个不知道大小的数,这个位置可以二分),那么该下标就是第i个位置的数,将此位置改为0。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;int a[maxn], ans[maxn];//树状数组
int n, b[maxn];
void add(int x, int v){ for(int i = x; i <= n; i+=i&(-i))b[i]+=v;}//序列中第x个数加上k
int query(int x){ int ans=0;for(int i=x;i>0;i-=i&(-i))ans+=b[i];return ans;}//查询序列前x个数的和int main(){ios::sync_with_stdio(false);cin>>n;for(int i = 2; i <= n; i++)cin>>a[i];for(int i = 1; i <= n; i++)add(i,1);for(int i = n; i >= 1; i--){int l = 1, r = n;while(l < r){int mid = (l+r)>>1;if(query(mid)<a[i]+1)l = mid+1;else r = mid;}ans[i] = l;add(l,-1);}for(int i = 1; i <= n; i++)cout<<ans[i]<<"\n";return 0;
}

AcWing 1275. 最大数1125人打卡

//题意:维护一个空序列,m次操作,每次可以末尾添加一个数或询问后L个数的最大值。
//思路:最大值rmq可以线段树维护,对于不定长的n,可以建一颗m的树,然后维护当前长度n,每次修改位置n的值即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e5+10;//线段树
#define lch p<<1
#define rch p<<1|1
struct node{int l, r, v;}tr[maxn<<2];
void build(int p, int l, int r){tr[p] = {l, r, 0};if(l==r)return ;int mid = l+r>>1;build(lch, l, mid);build(rch, mid+1, r);
}
void update(int p, int x, int v){//修改位置x(即长为n的末尾)的值为v, 即单点修改if(tr[p].l==tr[p].r)tr[p].v=v;else{int mid = tr[p].l+tr[p].r>>1;if(x<=mid)update(lch, x, v);else update(rch, x, v);tr[p].v = max(tr[lch].v, tr[rch].v);//回溯}
}
int query(int p, int l, int r){ //查询[n-L+1, n]内的最大值if(tr[p].l>=l&&tr[p].r<=r)return tr[p].v;int mid = tr[p].l+tr[p].r>>1, res = 0;if(l<=mid)res = query(lch,l,r);if(r >mid)res = max(res, query(rch,l,r));return res;
}int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);int n=0, last=0; //当前长度n,上一次的值lastint m, mod;  cin>>m>>mod;build(1,1,m);while(m--){char op;  cin>>op;if(op=='A'){int x;  cin>>x;update(1, ++n,((LL)x+last)%mod);//WA}else{int L;  cin>>L;cout<<(last = query(1,n-L+1,n))<<"\n";}}return 0;
}

AcWing 245. 你能回答这些问题吗955人打卡

//题意:维护一个序列,支持单点修改和求区间最大子段和
//思路:
//+ 最大子段和的递归分治求法(将序列划分为左右两部分,则最大子段和可能在三处出现:左半部、右半部以及跨越左右边界的部分,l==r时为递归边界)
//+ 线段树维护:区间左右端点起以及整个区间的最大子段和,转移时候=max{左右儿子的mx,跨越左右儿子中点的子段和}
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 5e5+10;//线段树
struct Segment_Tree{struct node{LL ml, mr, mx, sum; //分别维护左右端点和区间的mx,以及区间sumvoid operator = (LL k){sum=ml=mr=mx=k;}node(){sum=ml=mr=mx=0;}}tr[maxn<<2|1];#define lch p<<1#define rch p<<1|1node pushup(node l, node r){node t;t.sum = l.sum + r.sum;t.ml = max(l.ml, l.sum+r.ml);t.mr = max(r.mr, r.sum+l.mr);t.mx = max(l.mr+r.ml,max(l.mx, r.mx));return t;}void build(LL p, LL l, LL r){if(l==r){int x; cin>>x; tr[p]=x;}else{LL mid = l+r>>1;build(lch, l, mid);build(rch, mid+1, r);tr[p] = pushup(tr[lch], tr[rch]);}}void update(LL p, LL l, LL r, LL x, LL k){if(l==r){if(l==x)tr[p] = k;return ;}LL mid = l+r>>1;if(x<=mid)update(lch, l, mid, x, k);if(x> mid)update(rch, mid+1, r, x, k);tr[p] = pushup(tr[lch], tr[rch]);}node query(LL p, LL l, LL r, LL ql, LL qr){if(ql<=l && r<=qr)return tr[p];LL mid = l+r>>1;if(qr<=mid)return query(lch, l, mid, ql, qr);if(ql > mid)return query(rch, mid+1, r, ql, qr);return pushup(query(lch, l, mid, ql, qr), query(rch, mid+1, r, ql, qr));}
}sgt;int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);int n, m;  cin>>n>>m;sgt.build(1,1,n);while(m--){int op;  cin>>op;if(op==1){int l, r;  cin>>l>>r;if(l>r)swap(l,r);cout<<sgt.query(1,1,n,l,r).mx<<"\n";}else{int x, v;  cin>>x>>v;sgt.update(1,1,n, x,v);}}return 0;
}

AcWing 246. 区间最大公约数762人打卡

//题意:维护一个序列,支持区间修改和求区间GCD
//思路:
//+ 对于修改,由gcd(a,b)=gcd(a,a-b)得gcd(a1,a2,..,an)=gcd(a1,a2-a1,a3-a2,..an-a_n-1),可以线段树维护原序列差分的gcd,区间修改即转换为单点修改。
//+ 对于查询,因为差分已经被修改了,所以最开始额外开一个树状数组维护差分,先查询前缀和后得到当前a[l]的值,再与区间[l+1,r]的gcd求公约数即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 5e5+10;
LL gcd(LL a, LL b){ return !b?a:gcd(b,a%b); }LL n, m;
LL a[maxn], d[maxn];//差分//树状数组
LL b[maxn];
void add(LL x, LL v){ for(LL i = x; i <= n; i+=i&(-i))b[i]+=v;}
LL ask(LL x){ LL ans=0;for(LL i=x;i>0;i-=i&(-i))ans+=b[i];return ans;}//线段树
#define lch p<<1
#define rch p<<1|1
struct node{LL l, r, val;}tr[maxn<<2];
void build(LL p, LL l, LL r){tr[p].l = l, tr[p].r = r;if(l==r){tr[p].val = d[l]; return ;}LL mid = l+r>>1;build(lch, l, mid);build(rch, mid+1, r);tr[p].val = gcd(tr[lch].val, tr[rch].val);
}
void change(LL p, LL x, LL v){if(tr[p].l == tr[p].r){tr[p].val += v; return ;}LL mid = tr[p].l+tr[p].r>>1;if(x <= mid)change(lch, x, v);else change(rch, x, v);tr[p].val = gcd(tr[lch].val, tr[rch].val);
}
LL query(LL p, LL ql, LL qr){if(ql<=tr[p].l && qr>=tr[p].r)return tr[p].val;LL mid = tr[p].l+tr[p].r>>1, res = 0;if(ql <= mid)res = gcd(res, query(lch, ql, qr));if(qr > mid) res = gcd(res, query(rch, ql, qr));return abs(res);//防止负数
}int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin>>n>>m;for(int i = 1; i <= n; i++){cin>>a[i];  d[i]=a[i]-a[i-1];}build(1,1,n);while(m--){string op;  cin>>op;if(op=="Q"){LL l, r;  cin>>l>>r;cout<<gcd(a[l]+ask(l), query(1,l+1,r) )<<"\n";}else{LL l, r, val;  cin>>l>>r>>val;add(l, val);  add(r+1,-val);change(1,l,val); if(r<n)change(1,r+1,-val);//防止n+1越界}}return 0;
}

AcWing 243. 一个简单的整数问题2 845人打卡

//题意:维护一个序列,支持区间加值和区间询问
//思路:线段树打lazy tag即可
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5+10;LL n, q, a[maxn];//线段树
#define lch p<<1
#define rch p<<1|1
struct node{int l, r; LL sum, tag;}tr[maxn<<2];
void build(int p, int l, int r){tr[p].l=l, tr[p].r = r;if(l==r){tr[p].sum = a[l];  tr[p].tag = 0;return ;}int mid = l+r>>1;build(lch, l, mid);build(rch, mid+1, r);tr[p].sum = tr[lch].sum + tr[rch].sum;
}
LL query(int p, int ql, int qr){if(ql<=tr[p].l && tr[p].r<=qr)return tr[p].sum;int mid = tr[p].l+tr[p].r>>1;LL res = tr[p].tag*(min(tr[p].r, qr)- max(tr[p].l, ql)+1);//pushdown_计算懒标记的影响if(ql <= mid)res += query(lch, ql, qr);if(qr >= mid+1)res += query(rch, ql, qr);return res;
}
void change(int p, int cl, int cr, int cd){if(cl<=tr[p].l && tr[p].r <= cr){tr[p].sum += cd*(tr[p].r-tr[p].l+1);tr[p].tag += cd;return ;}int mid = tr[p].l+tr[p].r>>1;if(cl <= mid)change(lch, cl, cr, cd);if(cr >= mid+1)change(rch, cl, cr,cd);tr[p].sum = tr[lch].sum + tr[rch].sum+tr[p].tag*(tr[p].r-tr[p].l+1); //pushup_懒标记也算上
}int main(){ios::sync_with_stdio(false);cin>>n>>q;for(int i = 1; i <= n; i++)cin>>a[i];build(1,1,n);while(q--){string op;  cin>>op;if(op=="Q"){int l, r;  cin>>l>>r;cout<<query(1,l,r)<<"\n";}else{int l, r, x;  cin>>l>>r>>x;change(1,l,r,x);}}return 0;
}

AcWing 247. 亚特兰蒂斯482人打卡

//题意:给出平面中的n个长方形,求总面积.
//思路:扫描线求矩形面积并板子
#include<bits/stdc++.h>
typedef double LL;
using namespace std;
const int maxn = 1e5+10;//扫描线: 维护线段端点X和线段信息line[l,r,h,(1/-1)]
LL X[maxn<<1];
struct Scan{ LL l, r, h; int mark; }line[maxn<<2];
bool operator < (Scan a,Scan b){return a.h<b.h;}//线段树: sum维护被覆盖次数, len维护区间被截长度
#define lch (p<<1)
#define rch (p<<1|1)
struct Sgt{ int l, r, sum; LL len; }tree[maxn<<2];
void build(int p, int l, int r){tree[p] = Sgt{l,r,0,0};if(l==r)return ;int mid = l+r>>1;build(lch, l, mid);build(rch, mid+1, r);return ;
}
void push_up(int p){int l = tree[p].l, r = tree[p].r;if(tree[p].sum!=0){ //被覆盖tree[p].len = X[r+1]-X[l];//更新长度,将线段树节点x的[l,r]与实际区间[X[l],X[r+1]]对应,保证了叶节点不是一个端点}else{tree[p].len = tree[lch].len+tree[rch].len;//合并儿子信息}
}
void update(int p, LL L, LL R, int c){int l = tree[p].l, r = tree[p].r;if(X[r+1]<=L || R<=X[l])return ;//考虑[2,5],[5,8]两条线段, 要修改[1,5]区间的sum//虽然5在这个区间内,[5,8] 却并不是我们希望修改的线段if(L <= X[l] && X[r+1]<=R){tree[p].sum += c;push_up(p);return ;}update(lch,L,R,c);update(rch,L,R,c);push_up(p);
}int main(){//ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);int n, cnt=0;while(cin>>n &&n){memset(tree,0,sizeof(tree));cout<<"Test case #"<<++cnt<<"\n";for(int i = 1; i <= n; i++){LL x1, y1, x2, y2;cin>>x1>>y1>>x2>>y2;X[2*i-1]=x1, X[2*i]=x2;line[2*i-1] = Scan{x1,x2,y1,1};line[2*i] = Scan{x1,x2,y2,-1};}n <<= 1;  //一共有2*n条线段sort(line+1, line+n+1);sort(X+1, X+n+1);       //端点排序int tot = unique(X+1,X+n+1)-X-1; //端点去重离散化,建立端点[l,r]与[1,n]的对应关系build(1,1,tot-1);      //用线段端点[X[1], X[tot]]建线段树LL ans = 0;for(int i = 1; i < n; i++){//最后一条边不用update(1,line[i].l,line[i].r, line[i].mark);//扫描信息ans += tree[1].len*(line[i+1].h-line[i].h);//统计面积}//cout<<ans<<"\n";printf("Total explored area: %.2lf\n\n",ans);}return 0;
}

AcWing 1277. 维护序列638人打卡

//题意:维护一个序列,支持区间+x,区间*x,查询区间和
//思路:维护两个LazyTag
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 100010;
typedef long long LL;int n, m;
LL a[maxn],mod;
struct node{int l, r;LL val, addmark, mulmark;
}sgt[maxn<<2];
void build(int p, int l, int r){sgt[p].l = l, sgt[p].r = r;sgt[p].mulmark=1, sgt[p].addmark=0;if(l == r){sgt[p].val = a[l];}else{int m = (l+r)/2;build(p*2,l,m);build(p*2+1,m+1,r);sgt[p].val = sgt[p*2].val+sgt[p*2+1].val;}sgt[p].val %= mod;
}
void pushdown(int p){if(sgt[p].addmark==0&&sgt[p].mulmark==1)return ;//初始化父节点LL t1 = sgt[p].addmark, t2 = sgt[p].mulmark;sgt[p].addmark = 0, sgt[p].mulmark = 1;//维护标记sgt[p*2].mulmark = (sgt[p*2].mulmark*t2)%mod;sgt[p*2+1].mulmark = (sgt[p*2+1].mulmark*t2)%mod;sgt[p*2].addmark = (sgt[p*2].addmark*t2+t1)%mod;sgt[p*2+1].addmark = (sgt[p*2+1].addmark*t2+t1)%mod;//更新当前值,我们规定乘法优先更新(加法优先会损失精度)int l = sgt[p].l, r = sgt[p].r, m = (l+r)/2;sgt[p*2].val=(sgt[p*2].val*t2+t1*(m-l+1))%mod;//先乘以乘法标记再加上已用乘法标记更新过的加法标记。sgt[p*2+1].val=(sgt[p*2+1].val*t2+t1*(r-m))%mod;
}
void add(int p, int l, int r, LL v){if(l <= sgt[p].l && sgt[p].r <= r){sgt[p].val = (sgt[p].val+(sgt[p].r-sgt[p].l+1)*v)%mod;sgt[p].addmark = (sgt[p].addmark+v)%mod;return ;}pushdown(p);int m = (sgt[p].l+sgt[p].r)/2;if(l <= m)add(p*2,l,r,v);if(r > m)add(p*2+1,l,r,v);sgt[p].val = (sgt[p*2].val+sgt[p*2+1].val)%mod;
}
void times(int p, int l, int r, LL v){if(l <= sgt[p].l && sgt[p].r <= r){sgt[p].val = (sgt[p].val*v)%mod;sgt[p].mulmark = (sgt[p].mulmark*v)%mod;sgt[p].addmark = (sgt[p].addmark*v)%mod;//原先的加法标记也要乘return ;}pushdown(p);int m = (sgt[p].l+sgt[p].r)/2;if(l <= m)times(p*2,l,r,v);if(r > m)times(p*2+1,l,r,v);sgt[p].val = (sgt[p*2].val+sgt[p*2+1].val)%mod;
}
LL query(int p, int l, int r){if(l <= sgt[p].l && sgt[p].r <= r)return sgt[p].val;pushdown(p); //pushdownLL m = (sgt[p].l+sgt[p].r)/2, ans = 0;if(l <= m)ans += query(p*2,l,r);if(r > m)ans += query(p*2+1,l,r);return ans%mod;
}int main(){ios::sync_with_stdio(false);cin>>n>>mod;for(int i = 1; i <= n; i++)cin>>a[i];build(1,1,n);cin>>m;for(int i = 1; i <= m; i++){int op;  cin>>op;if(op == 1){LL x, y, z;  cin>>x>>y>>z;times(1,x,y,z);}else if(op == 2){LL x, y, z;  cin>>x>>y>>z;add(1,x,y,z);}else{LL x, y;  cin>>x>>y;cout<<query(1,x,y)%mod<<"\n";}}return 0;
}

AcWing 256. 最大异或和388人打卡

//题意:维护一个序列,支持末尾加一个数,询问区间[l,r]中的p,满足a[p]^a[p+1]...^a[n]^x最大。
//思路:
//+ 首先因为区间异或和,且异或满足性质a^b^a==b,所以可以想到异或前缀和,s[l~r] = s[r]^s[l-1]。题目转化为求a[p-1]^s[n]^x最大。
//+ 然后v=s[n]^x为定值,即为求[l-1,r-1]中与之异或最大的值,考虑二进制最高位开始到最低位的每一个数字尽量与v的二进制对应位相反就可以保证异或出来最大。
//+ 综上,可以将所有s[i]加入01字典树中,用于寻找最大异或,对于区间查询,采用可持久化维护。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 6e5+10;
typedef long long LL;int n, m, s[maxn];//可持久化01字典树
int tire[maxn*24][2], rt[maxn*24], idx, ver[maxn*24];
//第i个版本,上一个版本,当前版本的对应节点,当前这个数第几位
void insert(int i, int p, int q, int k){if(k<0){ //记录结束了ver[q] = i;//记录当前节点(可能会被后面公用)所能到达的最大范围ireturn ;}int c = s[i]>>k&1;//取出前缀和的二进制表示从右往左数第k位vif(p)tire[q][c^1] = tire[p][c^1];//如果前一个节点存在当前节点没有的分支, 那就把当前节点的这个空的路径指过去, 相当于复制tire[q][c] = ++idx;//正常trie树插入insert(i,tire[p][c],tire[q][c],k-1);//递归插入下一位二进制ver[q] = max(ver[tire[q][0]], ver[tire[q][1]]);
}
int query(int l, int r, int C){int p = rt[r];for(int i = 23; i >= 0; i--){int v = C>>i&1;if(ver[tire[p][v^1]] >= l)p = tire[p][v^1];else p = tire[p][v];}return C^s[ver[p]];
}int main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);cin>>n>>m;s[0] = 0, ver[0] = -1;rt[0] = ++idx, insert(0,0,rt[0],23);for(int i = 1; i <= n; i++){int x;  cin>>x;  s[i]=s[i-1]^x;rt[i] = ++idx;  insert(i,rt[i-1],rt[i], 23);}while(m--){string op;  cin>>op;if(op=="A"){int x;  cin>>x;n++;  s[n] = s[n-1]^x;rt[n] = ++idx;  insert(n,rt[n-1],rt[n], 23);}else{int l, r, x;  cin>>l>>r>>x;cout<<query(l-1, r-1, s[n]^x)<<"\n";}}return 0;
}

AcWing 255. 第K小数428人打卡

//题意:区间静态第k小。有n个数,多次询问一个区间[L,R]中第k小的值是多少。
//思路:可持久化线段树 2(主席树)模板
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;int a[maxn], b[maxn];int rt[maxn], lch[maxn<<5], rch[maxn<<5], sum[maxn<<5], tot;//主席树
void build(int &p, int l, int r){p = ++tot;if(l==r)return ;int mid = (l+r)>>1;build(lch[p], l, mid);build(rch[p], mid+1, r);
}
int update(int p, int l, int r, int x){ //b[x]+=1;int np = ++tot;lch[np]=lch[p], rch[np] = rch[p], sum[np] = sum[p]+1;if(l==r)return np;int mid = (l+r)>>1;if(x<=mid)lch[np] = update(lch[np], l, mid, x);else rch[np] = update(rch[np], mid+1, r, x);return np;
}
int query(int u, int v, int l, int r, int k){int ans, mid=(l+r>>1), x=sum[lch[v]]-sum[lch[u]];if(l==r)return l;if(x>=k)ans = query(lch[u], lch[v], l, mid, k);else ans = query(rch[u], rch[v], mid+1, r, k-x);return ans;
}int main(){int n, q;  cin>>n>>q;for(int i = 1; i <= n; i++)cin>>a[i], b[i]=a[i];sort(b+1,b+n+1);int m = unique(b+1,b+n+1)-b-1;build(rt[0], 1, m);                        //用离散化后的数据建立初始的值域线段树,区间加维护每个元素(位置)的出现次数for(int i = 1; i <= n; i++){int p = lower_bound(b+1,b+m+1, a[i])-b;//查询a[i]离散化后对应的值rt[i] = update(rt[i-1], 1, m, p);     //建立第i个版本的值域线段树,维护[1,i]时,每个区间的元素个数}while(q--){int l, r, k;  cin>>l>>r>>k;int ans = query(rt[l-1],rt[r],1,m,k); //查询用第r个版本和第l-1个版本之间值域差值查cout<<b[ans]<<"\n";                    //返回离散化对应的值}return 0;
}

AcWing 253. 普通平衡树415人打卡

#include<bits/stdc++.h>
using namespace std;
int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);int T;  cin>>T;vector<int>a;while(T--){int op, x;  cin>>op>>x;if(op==1){a.insert(upper_bound(a.begin(),a.end(),x),x);}else if(op==2){a.erase(lower_bound(a.begin(),a.end(),x));}else if(op==3){cout<<lower_bound(a.begin(),a.end(),x)-a.begin()+1<<endl;}else if(op==4){cout<<a[x-1]<<endl;}else if(op==5){cout<<*--lower_bound(a.begin(),a.end(),x)<<endl;}else{cout<<*upper_bound(a.begin(),a.end(),x)<<endl;}}return 0;
}

AcWing 265. 营业额统计344人打卡

//题意:定义第i天的最小波动值为min{|a[i]-a[j]|, j<i},求每天的最小波动值和
//思路:set维护之前的营业额,每次二分前驱和后继,暴力计算。
#include<bits/stdc++.h>
using namespace std;
int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);int n;  cin>>n;set<int>se;se.insert(-1e9); se.insert(1e9);int ans = 0;for(int i = 1; i <= n; i++){int x;  cin>>x;if(i==1){ ans += x;  se.insert(x); }if(se.count(x)){//波动为0continue;}else{auto a = se.lower_bound(x);//找>=x的第一个数if(*a != x){               //!=x,所以是大于x的第一个数auto b = a;  b--;     //小于x的第一个数ans += min(abs(*b-x), abs(*a-x)); //前驱和后继取最小se.insert(x);}}}cout<<ans<<"\n";return 0;
}

AcWing 1282. 搜索关键词441人打卡

//题意:给定n个长度不超过50的单词,以及一篇长为m的文章,问有多少个单词在文章中出现。
//思路:单词丢进自动机建fail树,然后拿文章匹配一下。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4+10;
char s[maxn*100];
int ch[maxn*50][26], cnt[maxn*50], tot, fail[maxn*50];
struct ac{void ins(){//字典树scanf("%s",s+1); int now=0, len=strlen(s+1);for(int i = 1; i <= len; i++){int u = s[i]-'a';if(!ch[now][u])ch[now][u] = ++tot;now = ch[now][u];}cnt[now]++;}void build(){//fail树queue<int>q;for(int i=0; i < 26; i++)if(ch[0][i])q.push(ch[0][i]);while(q.size()){int x = q.front(); q.pop();for(int i = 0; i < 26; i++){int y = ch[x][i];if(y){fail[y] = ch[fail[x]][i];q.push(y);}else{ch[x][i] = ch[fail[x]][i];}}}}void solve(){//匹配int res = 0;int len = strlen(s+1), now = 0;for(int i=1; i<=len; i++){int u = s[i]-'a';now = ch[now][u];int p = now;while(p){res += cnt[p];cnt[p] = 0;p = fail[p];}}cout<<res<<"\n";}void init(){memset(ch,0,sizeof(ch));memset(cnt,0,sizeof(cnt));memset(fail,0,sizeof(fail));}
}ac;int main(){//ios::sync_with_stdio(0);//, cin.tie(0), cout.tie(0);WAint T;  cin>>T;while(T--){ac.init();int n;  cin>>n;for(int i = 1; i <= n; i++)ac.ins();ac.build();scanf("%s",s+1);ac.solve();}return 0;
}

AcWing 1285. 单词297人打卡

//题意:给出n个单词和n个与之相同的模式串进行匹配,求出各个单词匹配的出现次数
//思路:跑一遍AC自动机,每一个节点保存一下属于多少字符串,为它的权值。然后一个节点表示的字符串在整个字典中出现的次数相当于其在Fail树中的子树的权值的和
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1100005;int n;
char s[maxn];
int ch[maxn][26], sz[maxn], cnt;
int a[maxn], h[maxn], fail[maxn];
struct ac{void ins(int x){scanf("%s",s+1); int now=0, len=strlen(s+1);for(int i = 1; i <= len; i++){int u = s[i]-'a';if(!ch[now][u])ch[now][u] = ++cnt;now = ch[now][u];sz[now]++;}a[x] = now;}void build(){int head=0, tail = 0;for(int i=0; i < 26; i++)if(ch[0][i])h[++tail]=ch[0][i];while(head < tail){int x=h[++head], y;for(int i = 0; i < 26; i++){if(ch[x][i]){y = ch[x][i];h[++tail] = y;fail[y] = ch[fail[x]][i];}else{ch[x][i] = ch[fail[x]][i];}}}}void solve(){for(int i=cnt; i >= 0; i--)sz[fail[h[i]]]+=sz[h[i]];for(int i = 1; i <= n; i++)cout<<sz[a[i]]<<"\n";}
}ac;int main(){//ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);WAcin>>n;for(int i=1; i<=n; i++)ac.ins(i);ac.build();ac.solve();return 0;
}

AcWing提高算法课Level-3 第四章 高级数据结构相关推荐

  1. AcWing提高算法课Level-3 第六章 基础算法

    AcWing提高算法课Level-3 第六章 基础算法 位运算 AcWing 90. 64位整数乘法761人打卡 递推与递归 AcWing 95. 费解的开关520人打卡 AcWing 97. 约数之 ...

  2. AcWing算法提高课 Level-3 第四章 高级数据结构

    并查集 1250. 格子游戏 并查集解决的是连通性(无向图联通分量)和传递性(家谱关系)问题,并且可以动态的维护.抛开格子不看,任意一个图中,增加一条边形成环当且仅当这条边连接的两点已经联通,于是可以 ...

  3. 第四章 高级数据结构

    目录 并查集 1250. 格子游戏[并查集判环] 1252. 搭配购买[并查集 + 01背包] 237. 程序自动分析[并查集的扩展] 239. 奇偶游戏[不会] 238. 银河英雄传说[维护距离的并 ...

  4. AcWing基础算法课Level-2 第四讲 数学知识

    AcWing基础算法课Level-2 第四讲 数学知识 您将学会以下数学名词 质数,试除法,埃式筛法,线性筛,辗转相除,算术基本定理,质因数分解,欧拉函数,快速幂,费马小定理,逆元,拓展欧几里得,一次 ...

  5. AcWing进阶算法课Level-4 第七章 基础算法

    AcWing进阶算法课Level-4 第七章 基础算法 启发式合并 AcWing 2154. 梦幻布丁73人打卡 AcWing 3189. Lomsat gelral54人打卡 manacher算法 ...

  6. AcWing进阶算法课Level-4 第六章 搜索 (模拟退火,爬山)

    AcWing进阶算法课Level-4 第六章 搜索 模拟退火 AcWing 3167. 星星还是树110人打卡 AcWing 2424. 保龄球78人打卡 AcWing 2680. 均分数据72人打卡 ...

  7. AcWing基础算法课Level-2 第五讲 动态规划

    AcWing基础算法课Level-2 第五讲 动态规划 背包问题 AcWing 2. 01背包问题3018人打卡 AcWing 3. 完全背包问题2749人打卡 AcWing 4. 多重背包问题255 ...

  8. AcWing基础算法课Level-2 第二讲 数据结构

    AcWing基础算法课Level-2 第二讲 数据结构 单链表 AcWing 826. 单链表3453人打卡 双链表 AcWing 827. 双链表2865人打卡 栈 AcWing 828. 模拟栈3 ...

  9. AcWing基础算法课Level-2 第六讲 贪心

    AcWing基础算法课Level-2 第六讲 贪心 区间问题 AcWing 905. 区间选点1751人打卡 AcWing 908. 最大不相交区间数量1613人打卡 AcWing 906. 区间分组 ...

最新文章

  1. TypeScript 从听说到入门(上篇)
  2. R语言unique函数计算数据对象(vector、dataframe)的unique独特值:unique函数从vector向量、dataframe中删除重复项、删除dataframe重复行
  3. DNA RNA 蛋白质
  4. 做机器学习项目的checklist
  5. 火狐浏览器修改userAgent
  6. Hbase问题汇总与解答
  7. 【安全-相关】kerberos hadoop Login failure for hadoop/localhost@YOUNG.COM from keytab
  8. selenium自动化测试-1.selenium介绍及环境安装
  9. js判断数据类型(如数组)及数组操作函数
  10. HTML中常用的列表标签
  11. java 单个session过期_session过期的三种方法
  12. FPGA语法篇——Verilog 语法知识
  13. 【Python_绘图】堆积柱形图
  14. 服务器是用集成网卡好还是独立网卡好
  15. Web前端学习上----(案例实现)
  16. 将svg编译成字体图标
  17. redis:客户端管理
  18. python勾股定理、0-30_求助python大神,显示Process finished with exit code 0.
  19. 腾讯云修改邮箱登录方式
  20. 利用阿里大鱼发送短信验证

热门文章

  1. matlab 集合操作
  2. 实用的 Python —— 快速进行相关计算
  3. Hive 基础及安装
  4. java mysql jdbc封装类_Java-jdbc-封装类形式的数据库操作
  5. 为何python不好找工作-学完Python,为什么还找不到工作?现实很残酷!
  6. 怎么自学python-结合学习经历,谈一谈如何学习Python
  7. 自学python考哪些证书-学Python能挣多少钱?哪些人适合学Python?
  8. 自学python能找到工作吗-自学Python如何找工作?多久能找到工作?
  9. python就业方向-Python的5大就业方向,薪资诱人前景好!
  10. python基础教程免费下载-Python基础教程(第2版)