数据结构
目录
- P3378 【模板】堆
- P3377 【模板】左偏树(可并堆)
- P1886 滑动窗口 /【模板】单调队列:
- 单调栈 P5788 【模板】单调栈
- 悬线法 P4147 玉蟾宫
- P1801 黑匣子(对顶堆,可求第i大,如中位数等):
- P3372 【模板】线段树 1:
- 线段树变种1
- 吉如一线段树? P4314 CPU 监控
- 主席树 P3919 【模板】可持久化线段树 1(可持久化数组)
- 主席树应用:区间第k小 P3834 【模板】可持久化线段树 2
- 李超线段树 P4097 [HEOI2013]Segment
- 线段树优化建图 CF786B Legacy
- 线段树合并 P3224 [HNOI2012]永无乡
- 二维偏序 poj2353 Stars
- P5854 【模板】笛卡尔树
- P3865 【模板】ST 表:
- 平衡树Treap P3369【模板】普通平衡树
- P3369【模板】普通平衡树 替罪羊树
- 莫队 P1494 [国家集训队] 小 Z 的袜子
P3378 【模板】堆
#include<bits/stdc++.h>
#define for1(i,a,b) for(int i = a;i<=b;i++)
using namespace std;
int d[5000005],tail;
void put(int x)
{
int now=++tail;
d[tail]=x;
while(now>1)
{
if(d[now]>=d[now/2])return ;
else swap(d[now],d[now/2]);
now/=2;
}
return ;
}
void pop()
{
int ans=d[1];
d[1]=d[tail--];
int now=1;
while(now*2<=tail)
{
int nex=now*2;
if(nex<tail&&d[nex+1]<d[nex]) nex++;
if(d[now]<=d[nex]) return ;
swap(d[now],d[nex]);
now=nex;
}
}
int n,x,y;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
if(x==1) scanf("%d",&y),put(y);
if(x==2) printf("%d\n",d[1]);
if(x==3) pop();
}
return 0;
}
P3377 【模板】左偏树(可并堆)
#include<bits/stdc++.h>
#define for1(i,a,b) for(int i=a;i<=b;i++)
#define mp(a,b) make_pair(a,b)
#define ll long long
using namespace std;
const int maxn = 5e5 + 5;
int T;
struct node{
int id;
int val;
}a[maxn];
int n,m;
int ls[maxn],rs[maxn],rt[maxn],dis[maxn];//此处的dis不是普通的dis
bool tf[maxn];
bool operator<(const node &x,const node &y)
{
return x.val==y.val?x.id<y.id:x.val<y.val;
}
int find(int x)
{
if(rt[x] == x) return x;
return rt[x] = find(rt[x]);
}
int merge(int x,int y)
{
if(!x||!y)
return x+y;
if(a[y] < a[x])swap(x,y);
rs[x] = merge(rs[x],y);
if(dis[ls[x]] < dis[rs[x]]) swap(ls[x],rs[x]);
dis[x]=dis[rs[x]]+1;
return x;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
dis[0] = -1;
cin >> n >> m;
for1(i,1,n)
cin >>a[i].val;
for1(i,1,n)
rt[i]=i,a[i].id = i;
int ji,x,y;
for1(i,1,m)
{
cin >> ji >> x;
if(ji == 1)
{
cin >> y;
if(tf[x] == 1 || tf[y] == 1) continue;
x = find(x);
y = find(y);
if(x != y)
rt[x] = rt[y] = merge(rt[x],rt[y]);
}
else
{
if(tf[x] == 1)
{
cout << -1 << '\n';
continue;
}
x = find(x);
cout << a[x].val << '\n';
tf[x] = 1;
rt[ls[x]] = rt[rs[x]] = rt[x] = merge(ls[x],rs[x]);
ls[x] = rs[x] = dis[x] = 0;
}
}
return 0;
}
P1886 滑动窗口 /【模板】单调队列:
stl版:
#include<bits/stdc++.h>
#define for1(i,a,b) for(int i = a;i <= b;i++)
using namespace std;
struct node{
int id;
int data;
} t;
deque <node> dl;
deque <node> dl2;
int m,n,ans,k,ji[1000000],a;
void ru(int x,int y)
{
while(!dl.empty() && dl.back().data >= x)
{
dl.pop_back();
}
t.data = x;
t.id = y;
dl.push_back(t);
}
void ru2(int x,int y)
{
while(!dl2.empty() && dl2.back().data <= x)
{
dl2.pop_back();
}
t.data = x;
t.id = y;
dl2.push_back(t);
}
int main()
{
cin>>n>>k;
for1(i,1,k)
{
scanf("%d",&ji[i]);
ru(ji[i],i);
}
cout<<dl.front().data<<' ';
int now = 1;
for1(i,k+1,n)
{
if(now >= dl.front().id) dl.pop_front();
now ++;
scanf("%d",&ji[i]);
ru(ji[i],i);
cout<<dl.front().data<<' ';
}
cout<<endl;
for1(i,1,k)
{
ru2(ji[i],i);
}
cout<<dl2.front().data<<' ';
now = 1;
for1(i,k+1,n)
{
if(now >= dl2.front().id) dl2.pop_front();
now ++
ru2(ji[i],i);
cout<<dl2.front().data<<' ';
}
return 0;
}
手打队列版:
#include<bits/stdc++.h>
#define for1(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
int a[5000005],q[5000005][2],hd=2,tl=1,n,k;
int main()
{
cin>>n>>k;
for1(i,1,n) cin>>a[i];
for1(i,1,k)
{
while(hd<=tl&&q[tl][0]>=a[i]) tl--;
q[++tl][0]=a[i];
q[tl][1]=i;
}
cout<<q[hd][0]<<' ';
for1(i,k+1,n)
{
while(hd<=tl&&i-k>=q[hd][1]) hd++;
while(hd<=tl&&q[tl][0]>a[i]) tl--;
q[++tl][0]=a[i];
q[tl][1]=i;
cout<<q[hd][0]<<' ';
}
hd=2,tl=1;
cout<<endl;
for1(i,1,k)
{
while(hd<=tl&&q[tl][0]<=a[i]) tl--;
q[++tl][0]=a[i];
q[tl][1]=i;
}
cout<<q[hd][0]<<' ';
for1(i,k+1,n)
{
while(hd<=tl&&i-k>=q[hd][1]) hd++;
while(hd<=tl&&q[tl][0]<a[i]) tl--;
q[++tl][0]=a[i];
q[tl][1]=i;
cout<<q[hd][0]<<' ';
}
return 0;
}
单调栈 P5788 【模板】单调栈
#include<bits/stdc++.h>
#define for1(i,a,b) for(int i = a;i<=b;i++)
#define ll long long
#define mp(a,b) make_pair(a,b)
using namespace std;
int n,a[3000005],f[3000005];
stack<int>s;
int main()
{
scanf("%d",&n);
for1(i,1,n) scanf("%d",&a[i]);
for(int i=n;i>=1;i--)
{
while(!s.empty()&&a[s.top()]<=a[i])
s.pop();
if(s.empty())
f[i]=0;
else f[i]=s.top();
s.push(i);
}
for1(i,1,n) printf("%d ",f[i]);
return 0;
}
悬线法 P4147 玉蟾宫
可以被单调栈取代,但是比单调栈好写的多
#include <bits/stdc++.h>
#define ll long long
#define for1(i,a,b) for(int i = a;i <= b;i ++)
using namespace std;
const int maxn = 5e3 + 5;
int l[maxn][maxn], n, m, r[maxn][maxn], h[maxn][maxn];
int a[maxn][maxn],ans;
int main()
{
cin >> n >> m;
char s;
for1(i,1,n)
for1(j,1,m)
{
cin >> s;
if(s == 'R')
a[i][j] = 1;
else h[i][j] = 1;
l[i][j] = j;
r[i][j] = j;
}
for1(i,1,n)
for1(j,2,m)
{
if(a[i][j] == 0 && a[i][j - 1] == 0)
l[i][j] = l[i][j - 1];
}
for1(i,1,n)
for(int j = m - 1;j;j --)
{
if(a[i][j] == 0 && a[i][j + 1] == 0)
r[i][j] = r[i][j + 1];
}
for1(i,1,n)
for1(j,1,m)
{
if(i>1&&a[i][j] == 0&&a[i-1][j] == 0)
{
l[i][j]=max(l[i][j],l[i-1][j]);
r[i][j]=min(r[i][j],r[i-1][j]);
h[i][j]=h[i-1][j]+1;
}
ans=max(ans,(r[i][j]-l[i][j]+1)*h[i][j]);
}
cout << ans * 3 <<'\n';
return 0;
}
P1801 黑匣子(对顶堆,可求第i大,如中位数等):
#include<bits/stdc++.h>
#define for1(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
using namespace std;
priority_queue<int > da;
priority_queue<int,vector<int>,greater<int> > xiao;
int n,m,r=1,a[500005],ji;
int main()
{
cin>>n>>m;
for1(i,1,n) cin>>a[i];
for1(i,1,m)
{
cin>>ji;
for1(j,r,ji)
{
da.push(a[j]);
if(da.size()==i) xiao.push(da.top()),da.pop();
}
r=ji+1;
cout<<xiao.top()<<endl;
da.push(xiao.top());
xiao.pop();
}
return 0;
}
P3372 【模板】线段树 1:
#include <bits/stdc++.h>
#define ll long long
#define ls q << 1
#define rs q << 1 | 1
#define for1(i,a,b) for(ll i = a;i <= b;i ++)
using namespace std;
const ll maxn = 1e6 + 5;
struct Segment{
ll val;
ll lan;
};
ll n,m,a[maxn];
struct SegmentTree{
Segment s[maxn * 4];
void Updata(ll q)
{
s[q].val = s[ls].val + s[rs].val;
}
void Build(ll q, ll l, ll r)
{
if(l == r)
{
s[q].val = a[l];
return ;
}
ll mid = (l + r) >> 1;
Build(ls,l,mid);
Build(rs,mid + 1,r);
Updata(q);
}
void LazyTag(ll q, ll l, ll r)
{
if(s[q].lan == 0) return ;
ll mid = (l + r) >> 1;
s[ls].val += (mid - l + 1) * s[q].lan;
s[rs].val += (r - (mid + 1) + 1) * s[q].lan;
s[ls].lan += s[q].lan;
s[rs].lan += s[q].lan;
s[q].lan = 0;
return ;
}
void Change(ll q, ll l, ll r, ll L, ll R, ll k)
{
if(L <= l && r <= R)
{
s[q].val += (r - l + 1) * k;
s[q].lan += k;
return ;
}
LazyTag(q,l,r);
ll mid = (l + r) >> 1;
if(L <= mid) Change(ls,l,mid,L,R,k);
if(R > mid) Change(rs,mid + 1,r,L,R,k);
Updata(q);
}
ll Query(ll q, ll l, ll r, ll L, ll R)
{
if(L <= l && r <= R)
return s[q].val;
LazyTag(q,l,r);
ll mid = (l + r) >> 1;
ll res = 0;
if(L <= mid) res += Query(ls,l,mid,L,R);
if(R > mid) res += Query(rs,mid + 1,r,L,R);
return res;
}
}Tree;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
for1(i,1,n)
cin >> a[i];
Tree.Build(1,1,n);
ll x,y,k,ji;
for1(i,1,m)
{
cin >> ji;
if(ji == 1)
{
cin >> x >> y >> k;
Tree.Change(1,1,n,x,y,k);
}
else
{
cin >> x >> y;
cout << Tree.Query(1,1,n,x,y) << '\n';
}
}
return 0;
}
压行版
#include <bits/stdc++.h>
#define ll long long
#define ls q << 1
#define rs q << 1 | 1
#define for1(i,a,b) for(ll i = a;i <= b;i ++)
using namespace std;
const ll maxn = 1e6 + 5;
struct Segment{ll val,lan;};
ll n,m,a[maxn];
struct SegmentTree{
Segment s[maxn * 4];
void Updata(ll q){s[q].val = s[ls].val + s[rs].val;}
void Build(ll q, ll l, ll r)
{
if(l == r) {s[q].val = a[l];return ;}
ll mid = (l + r) >> 1;
Build(ls,l,mid);
Build(rs,mid + 1,r);
Updata(q);
}
void LazyTag(ll q, ll l, ll r)
{
if(s[q].lan == 0) return ;
ll mid = (l + r) >> 1;
s[ls].val += (mid - l + 1) * s[q].lan;
s[rs].val += (r - (mid + 1) + 1) * s[q].lan;
s[ls].lan += s[q].lan;s[rs].lan += s[q].lan;
s[q].lan = 0;
return ;
}
void Change(ll q, ll l, ll r, ll L, ll R, ll k)
{
if(L<=l&&r<=R){s[q].val+=(r-l +1)*k,s[q].lan += k;return ; }
LazyTag(q,l,r);
ll mid = (l + r) >> 1;
if(L <= mid) Change(ls,l,mid,L,R,k);
if(R > mid) Change(rs,mid + 1,r,L,R,k);
Updata(q);
}
ll Query(ll q, ll l, ll r, ll L, ll R)
{
if(L <= l && r <= R)
return s[q].val;
LazyTag(q,l,r);
ll mid = (l + r) >> 1,res = 0;
if(L <= mid) res += Query(ls,l,mid,L,R);
if(R > mid) res += Query(rs,mid + 1,r,L,R);
return res;
}
}Tree;
int main()
{
ios::sync_with_stdio(false);cin.tie(0);
cin >> n >> m;
for1(i,1,n)cin >> a[i];
Tree.Build(1,1,n);
ll x,y,k,ji;
for1(i,1,m)
{
cin >> ji;
if(ji == 1)
cin >> x >> y >> k,
Tree.Change(1,1,n,x,y,k);
else
cin >> x >> y,
cout << Tree.Query(1,1,n,x,y) << '\n';
}
return 0;
}
线段树变种1

模板题所在的OJ要手机验证注册不了,所以这份代码不确定对不对
#include<bits/stdc++.h>
#define ll long long
#define ls q << 1
#define rs q << 1 | 1
#define for1(i,a,b) for(register ll i = a;i <= b;i ++)
using namespace std;
const int maxn = 1e6 + 5e5 + 5;
struct Segment{
int mx;
int cimx;
int cnt;
int sum;
int tag;
};
int a[maxn];
struct Tree{
Segment s[maxn<<2];
void push_up(int q)
{
s[q].sum = s[ls].sum + s[rs].sum;
if(s[ls].mx == s[rs].mx)
{
s[q].mx = s[ls].mx;
s[q].cimx = max(s[ls].cimx,s[rs].cimx);
s[q].cnt = s[ls].cnt + s[rs].cnt;
}
else if(s[ls].mx > s[rs].mx)
{
s[q].mx = s[ls].mx;
s[q].cimx = max(s[ls].cimx,s[rs].mx);
s[q].cnt = s[ls].cnt;
}
else if(s[ls].mx < s[rs].mx)
{
s[q].mx = s[rs].mx;
s[q].cimx = max(s[ls].mx,s[rs].cimx);
s[q].cnt = s[rs].cnt;
}
return ;
}
void push_tag(int q, int tg)
{
if(s[q].mx <= tg) return ;
s[q].sum += (tg - s[q].mx) * s[q].cnt;
s[q].mx = s[q].tag = tg;
return ;
}
void push_down(int q)
{
if(s[q].tag == -1) return ;
push_tag(ls,s[q].tag);
push_tag(rs,s[q].tag);
s[q].tag = -1;
return ;
}
void build(int q, int l, int r)
{
s[q].tag = -1;
if(l == r)
{
s[q].sum = a[l];
s[q].mx = a[l];
s[q].cimx = -1;
s[q].cnt = 1;
return ;
}
int mid = (l + r) >> 1;
build(ls,l,mid);
build(rs,mid + 1,r);
push_up(q);
return ;
}
void change(int q, int l, int r, int L, int R, int k)
{
if(s[q].mx <= k) return;
if(L <= l && r <= R && s[q].cimx < k)
{
push_tag(q,k);
return ;
}
push_down(q);
int mid = (l + r) >> 1;
if(L <= mid) change(ls,l,mid,L,R,k);
if(R > mid) change(rs,mid + 1,r,L,R,k);
push_up(q);
return ;
}
int query_mx(int q, int l, int r, int L, int R)
{
if(L <= l && r <= R)
{
return s[q].mx;
}
push_down(q);
int mid = (l + r) >> 1;
int res = -1;
if(L <= mid) res = max(res,query_mx(ls,l,mid,L,R));
if(R > mid) res = max(res,query_mx(rs,mid + 1,r,L,R));
return res;
}
int query_sum(int q, int l, int r, int L, int R)
{
if(L <= l && r <= R)
{
return s[q].sum;
}
push_down(q);
int mid = (l + r) >> 1;
int res = 0;
if(L <= mid) res += query_sum(ls,l,mid,L,R);
if(R > mid) res += query_sum(rs,mid + 1,r,L,R);
return res;
}
}Tree;
int n,m,T;
int main()
{
cin >> T;
while(T--)
{
cin >> n >> m;
for1(i,1,n) cin >> a[i];
Tree.build(1,1,n);
int ji, x, y, k;
for1(i,1,m)
{
cin >> ji >> x >> y;
if(ji == 0)
cin >> k,
Tree.change(1,1,n,x,y,k);
else if(ji == 1)
cout << Tree.query_mx(1,1,n,x,y) << '\n';
else
cout << Tree.query_sum(1,1,n,x,y) << '\n';
}
}
return 0;
}
吉如一线段树? P4314 CPU 监控
#include<bits/stdc++.h>
#define ll long long
#define ls q << 1
#define rs q << 1 | 1
#define for1(i,a,b) for(register ll i = a;i <= b;i ++)
using namespace std;
const int maxn = 5e5 + 5;
const int inf = 1e9 + 7;
struct Segment{
int mx;
int hmx;
int sum;
int hsum;
int val;//赋值之后区间加变成区间赋值
int hval;
int vis;//是否赋值过
};
int a[maxn];
struct Tree{
Segment s[maxn<<2];
void push_up(int q)
{
s[q].mx = max(s[ls].mx,s[rs].mx);
s[q].hmx = max(s[ls].hmx,s[rs].hmx);
return ;
}
void do_sum(int q, int k, int hk)//区间加减的下放
{
if(s[q].vis == 1)
{
s[q].hval = max(s[q].hval,s[q].val + hk);
s[q].hmx = max(s[q].hmx,s[q].mx + hk);
s[q].mx += k;
s[q].val += k;
return ;
//赋值过之后全部的操作都变成区间赋值
}
s[q].hsum = max(s[q].hsum,s[q].sum + hk);
s[q].hmx = max(s[q].hmx,s[q].mx + hk);
s[q].mx += k;
s[q].sum += k;
return ;
}//可理解为赋值前使用sum数组,赋值后使用val数组
void do_val(int q, int k, int hk)//区间赋值的下放
{
if(s[q].vis == 1)
{
s[q].hval = max(s[q].hval,hk);
s[q].hmx = max(s[q].hmx,hk);
}
else{
s[q].vis = 1;
s[q].hval = hk;
s[q].hmx = max(s[q].hmx,hk);
}
s[q].mx = s[q].val = k;
return ;
}
void push_down(int q)
{
do_sum(ls,s[q].sum,s[q].hsum);
do_sum(rs,s[q].sum,s[q].hsum);
s[q].sum = s[q].hsum = 0;
if(s[q].vis)
{
do_val(ls,s[q].val,s[q].hval);
do_val(rs,s[q].val,s[q].hval);
s[q].vis = 0;
s[q].val = s[q].hval = 0;
//他这个赋值操作搞完之后就没用了。。。
}
return ;
}
void build(int q, int l, int r)
{
if(l == r)
{
s[q].mx = a[l];
s[q].hmx = a[l];
return ;
}
int mid = (l + r) >> 1;
build(ls,l,mid);
build(rs,mid + 1,r);
push_up(q);
}
int query_mx(int q, int l, int r, int L, int R)
{
if(L <= l && r <= R)
{
return s[q].mx;
}
push_down(q);
int mid = (l + r) >> 1;
int res = -inf;
if(L <= mid) res = max(res,query_mx(ls,l,mid,L,R));
if(R > mid) res = max(res,query_mx(rs,mid + 1,r,L,R));
return res;
}
int query_hmx(int q, int l, int r, int L, int R)//历史最大值
{
if(L <= l && r <= R)
{
return s[q].hmx;
}
push_down(q);
int mid = (l + r) >> 1;
int res = -inf;
if(L <= mid) res = max(res,query_hmx(ls,l,mid,L,R));
if(R > mid) res = max(res,query_hmx(rs,mid + 1,r,L,R));
return res;
}
void change_jia(int q, int l, int r, int L, int R, int k)//区间加
{
if(L <= l && r <= R)
{
do_sum(q,k,k);
return ;
}
push_down(q);
int mid = (l + r) >> 1;
if(L <= mid) change_jia(ls,l,mid,L,R,k);
if(R > mid) change_jia(rs,mid + 1,r,L,R,k);
push_up(q);
}
void change_fuzhi(int q, int l, int r, int L, int R, int k)//区间赋值
{
if(L <= l && r <= R)
{
do_val(q,k,k);
return ;
}
push_down(q);
int mid = (l + r) >> 1;
if(L <= mid) change_fuzhi(ls,l,mid,L,R,k);
if(R > mid) change_fuzhi(rs,mid + 1,r,L,R,k);
push_up(q);
}
// void print(int q,int l,int r)
// {
// if(l==r)
// {
// cout << s[q].mx << '\n';
// return;
// }
// push_down(q);
// int mid = (l + r) >> 1;
// print(ls,l,mid);
// print(rs,mid+1,r);
// }
//
// inline void test(int n)
// {
// printf("=========================================\n");
// print(1,1,n);
// printf("\n=========================================\n");
// }
}Tree;
int n,m;
int main()
{
cin >> n;
for1(i,1,n)
cin >> a[i];
Tree.build(1,1,n);
cin >> m;
string c;
int x,y,z;
for1(i,1,m)
{
cin >> c;
cin >> x >> y;
if(c == "Q")
cout << Tree.query_mx(1,1,n,x,y) << '\n';
else if(c == "A")
cout << Tree.query_hmx(1,1,n,x,y) << '\n';
else if(c == "P")
cin >> z,
Tree.change_jia(1,1,n,x,y,z);
else
cin >> z,
Tree.change_fuzhi(1,1,n,x,y,z);
// Tree.test(n);
}
return 0;
}
主席树 P3919 【模板】可持久化线段树 1(可持久化数组)
#include <bits/stdc++.h>
#define ll long long
#define ls q << 1
#define rs q << 1 | 1
#define for1(i,a,b) for(ll i = a;i <= b;i ++)
using namespace std;
const ll maxn = 4e7 + 5;
struct Segment{
int l;
int r;
int val;
};
int a[maxn/40];
struct SegmentTree{
int root[maxn];
Segment s[maxn];
int cnt;
int clone(int x)//复制一个x节点
{
cnt ++;
s[cnt] = s[x];
return cnt;
}
int Build(int q,int l, int r)
{
q = ++ cnt;
if(l == r)
{
s[q].val = a[l];
return cnt;
}
int mid = (l + r) >> 1;
s[q].l = Build(s[q].l,l,mid);
s[q].r = Build(s[q].r,mid + 1,r);
return q;
}
int Change(int q, int l, int r, int x, int val)
{
q = clone(q);
if(l == r)
{
s[q].val = val;
return q;
}
int mid = (l + r) >> 1;
if(x <= mid)
s[q].l = Change(s[q].l,l,mid,x,val);
else
s[q].r = Change(s[q].r,mid + 1,r,x,val);
return q;
}
int Query(int q, int l, int r, int x)
{
if(l == r)
{
return s[q].val;
}
int mid = (l + r) >> 1;
if(x <= mid)
return Query(s[q].l,l,mid,x);
else
return Query(s[q].r,mid + 1,r,x);
}
}Tree;
int n,m;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
for1(i,1,n)
cin >> a[i];
Tree.root[0] = Tree.Build(0,1,n);
int x,y, rt, ji;
for1(i,1,m)
{
cin >> rt >> ji >> x;
if(ji == 1)
{
cin >> y;
Tree.root[i] = Tree.Change(Tree.root[rt],1,n,x,y);
}
else
{
cout << Tree.Query(Tree.root[rt],1,n,x) << '\n';
Tree.root[i] = Tree.root[rt];
}
}
return 0;
}
主席树应用:区间第k小 P3834 【模板】可持久化线段树 2
建树时每个版本代表着[1,i]的所有的数(权值线段树,区间[l,r]表示这个版本有多少个数的值域在[l,r]之间)
然后查询就是在主席树上二分
#include <bits/stdc++.h>
#define ll long long
#define ls q << 1
#define rs q << 1 | 1
#define for1(i,a,b) for(ll i = a;i <= b;i ++)
using namespace std;
const ll maxn = 2e5 + 5;
struct Segment{
int l;
int r;
int val;
};
int a[maxn];
struct SegmentTree{
int root[maxn << 5];
Segment s[maxn << 5];
int cnt;
int clone(int x)//复制一个x节点
{
cnt ++;
s[cnt] = s[x];
return cnt;
}
int Build(int q,int l, int r)
{
q = ++ cnt;
if(l == r)
{
s[q].val = 0;
return cnt;
}
int mid = (l + r) >> 1;
s[q].l = Build(s[q].l,l,mid);
s[q].r = Build(s[q].r,mid + 1,r);
return q;
}
int Change(int q, int l, int r, int x)
{
q = clone(q);
if(l == r)
{
s[q].val++;
return q;
}
int mid = (l + r) >> 1;
if(x <= mid)
s[q].l = Change(s[q].l,l,mid,x);
else
s[q].r = Change(s[q].r,mid + 1,r,x);
s[q].val = s[s[q].l].val + s[s[q].r].val;
return q;
}
int Query(int L,int R, int l, int r, int k)
{
int mid = (l + r) >> 1;
int x = s[s[R].l].val - s[s[L].l].val;
int ans;
if(l == r)
return l;
if(x >= k)
ans = Query(s[L].l, s[R].l, l, mid, k);
else
ans = Query(s[L].r, s[R].r, mid+1, r, k-x);
return ans;
}
}Tree;
int n,m;
int b[maxn];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
for1(i,1,n)
cin >> a[i],
b[i] = a[i];
sort(b + 1, b + n + 1);
int len = unique(b + 1, b + n + 1) - b - 1;
Tree.root[0] = Tree.Build(0,1,len);
for1(i,1,n)
{
int ji = lower_bound(b + 1,b + len + 1,a[i]) - b;
Tree.root[i] = Tree.Change(Tree.root[i - 1],1,len,ji);
}
int x,y, k;
for1(i,1,m)
{
cin >> x >> y >> k;
int ans = Tree.Query(Tree.root[x - 1],Tree.root[y],1,len,k);
cout << b[ans] << '\n';
}
return 0;
}
李超线段树 P4097 [HEOI2013]Segment
#include <bits/stdc++.h>
#define ll long long
#define for1(i,a,b) for(int i = a;i <= b;i ++)
#define pir pair<double,int>
#define fir first
#define sec second
#define ls q << 1
#define rs q << 1 | 1
#define mp(a,b) make_pair(a,b)
using namespace std;
const int maxn = 5e5 + 5;
const int inf = 1e9;
const int mod = 39989;
const double eps = 1e-12;
struct Line{
double k,b;
int x0,x1;
double Find(int x)
{
if(x0 <= x && x <= x1)
return k * 1.0 * x + b;
else return -inf;
}
}f[maxn];
struct Tree{
int s[maxn * 4];
pir mx(pir a, pir b)
{
if(a.fir -b.fir > eps) return a;
if(b.fir - a.fir > eps) return b;
if(a.sec < b.sec) return a;
return b;
}
void Updata(int q, int l, int r, int L, int R, int k)
{
if(L <= l && r <= R)
{
if(s[q] == 0)
{
s[q] = k;
return ;
}
int mid = (l + r) >> 1;
if(f[k].Find(mid) - f[s[q]].Find(mid) > eps)
//把答案改成比较大的,小的就继续传下去
swap(k,s[q]);
if(f[k].Find(l) - f[s[q]].Find(l) > eps
|| (f[k].Find(l) == f[s[q]].Find(l) && k < s[q]))
Updata(ls,l,mid,L,R,k);
if(f[k].Find(r) - f[s[q]].Find(r) > eps
|| (f[k].Find(r) == f[s[q]].Find(r) && k < s[q]))
Updata(rs,mid + 1,r,L,R,k);
return ;
}
int mid = (l + r) >> 1;
if(L <= mid) Updata(ls,l,mid,L,R,k);
if(R > mid) Updata(rs,mid + 1,r,L,R,k);
return ;
}
pir Query(int q, int l, int r, int k)
{
pir res;
if(s[q] != 0)
res = mp(f[s[q]].Find(k),s[q]);
if(l == r) return res;
int mid = (l + r) >> 1;
if(k <= mid) res = mx(res,Query(ls,l,mid,k));
else res = mx(res,Query(rs,mid + 1,r,k));
return res;
}
}Tree;
int n,cnt,m = 40000,lans;
int main()
{
ios::sync_with_stdio(false);cin.tie(0);
cin >> n;
int x0,x1,y0,y1,ji,k;
for1(i,1,n)
{
cin >> ji;
if(ji == 0)
{
cin >> k;
k = (k + lans - 1) % mod + 1;
lans = Tree.Query(1,1,m,k).sec;
cout << lans << '\n';
}
else{
cin >> x0 >>y0 >> x1 >> y1;
x0 = (x0 + lans - 1) % mod + 1;
x1 = (x1 + lans - 1) % mod + 1;
y0 = (y0 + lans - 1) % inf + 1;
y1 = (y1 + lans - 1) % inf + 1;
cnt++;
if(x0 == x1)//特判
{
f[cnt].k = 0;
f[cnt].b = max(y1,y0);
f[cnt].x0 = f[cnt].x1 = x0;
}
else{
f[cnt].k = 1.0 * (1.0 * y1 - y0)/(1.0 * x1 - x0);
f[cnt].b = 1.0 * (1.0 * y1 - f[cnt].k*x1);
f[cnt].x0 = min(x0,x1);
f[cnt].x1 = max(x0,x1);
}
Tree.Updata(1,1,m,min(x0,x1),max(x0,x1),cnt);
}
}
return 0;
}
线段树优化建图 CF786B Legacy
#include<bits/stdc++.h>
#define for1(i,a,b) for(register ll i=a;i<=b;i++)
#define ll long long
#define mp(a,b) make_pair(a,b)
#define lson p*2
#define rson p*2+1
using namespace std;
const ll N=3e6+5,K=5e5;
const ll inf = 0x3f3f3f3f3f3f3f3fll;
ll n,m,s,opt,w;
ll a[N],d[N];
bool v[N];
priority_queue<pair<ll,ll> >q;
struct node
{
int nex;
int to;
int w;
}tu[N*2];
int cnt,hd[N];
void add(ll x,ll y,ll z)
{
tu[++cnt].to = y;
tu[cnt].nex = hd[x];
tu[cnt].w = z;
hd[x] = cnt;
}
//虽说是用了线段树,但其实并没有真的建一棵线段树出来
//a的编号代表的就是线段树的区间的编号了
//不加的就是外向树,+K的那些是内向树
void build(ll p,ll l,ll r)
{
if(l==r)
{
a[l]=p;
return ;
}
ll mid=(l+r)/2;
add(p,lson,0);
add(p,rson,0);
//内向树自己给子孙连边
add((lson)+K,p+K,0);
add((rson)+K,p+K,0);
//外向树子孙给自己连边
build(lson,l,mid);
build(rson,mid+1,r);
}
void modify(ll p,ll l,ll r,ll L,ll R,ll v,ll w)
{
if(l>=L&&r<=R)
{
if(opt==2)
add(v+K,p,w);
//如果是区间连点,内向树区间向外向树点连边
else
add(p+K,v,w);
//如果是点连区间,则内向树点向外向树区间连边
return;
}
ll mid=(l+r)/2;
if(L<=mid)
modify(lson,l,mid,L,R,v,w);
if(R>mid)
modify(rson,mid+1,r,L,R,v,w);
}
void dij(ll s)
{
memset(d,0x3f,sizeof(d));
d[s]=0;
q.push(mp(0,s));
while(q.size())
{
ll x=q.top().second;
q.pop();
if(v[x])
continue;
v[x]=1;
for(ll i = hd[x];i;i = tu[i].nex)
{
ll v = tu[i].to, w = tu[i].w;
if(d[v] > d[x] + w)
{
d[v] = d[x] + w;
q.push(mp(-d[v],v));
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n>>m>>s;
build(1,1,n);
for1(i,1,n)
{
add(a[i],a[i]+K,0);
add(a[i]+K,a[i],0);
}
int x,y,z;
for1(i,1,m)
{
cin >> opt;
if(opt == 1)
{
cin>> x >> y >> z;
add(a[x] + K, a[y], z);
}
else
{
int l,r;
cin >>x >> l >> r >> w;
modify(1, 1, n, l, r, a[x], w);
}
}
dij(a[s] + K);//迪杰斯特拉
for1(i,1,n)
if(d[a[i]] != inf)
{
cout<<d[a[i]]<<' ';
}
else cout<<-1<<' ';
return 0;
}
线段树合并 P3224 [HNOI2012]永无乡
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define for1(i,a,b) for(register int i =a;i <= b;i++)
using namespace std;
const int maxn = 5e5 + 5;
struct segment{
int id;
int siz;
int l;
int r;
};
int n,m,fa[maxn],q;
int find(int x)
{
if(fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
struct Tree{
segment s[maxn << 2];
int rt[maxn];
int cnt;
int build(int q, int l, int r, int k, int x)
{
if(q == 0) q = ++cnt;
if(l == r)
{
s[q].id = x;
s[q].siz = 1;
return q;
}
int mid = (l + r) >> 1;
if(k <= mid) s[q].l = build(s[q].l,l,mid,k,x);
else s[q].r = build(s[q].r, mid + 1, r,k,x);
s[q].siz = s[s[q].l].siz + s[s[q].r].siz;
return q;
}
int hebing(int q1,int q2, int l, int r)//q2合并到q1
{
if(!q1 || !q2) return q1 + q2;
if(l == r)
{
if(s[q2].id)
{
s[q1].id = s[q2].id;
s[q1].siz += s[q2].siz;
}
return q1;
}
int mid = (l + r) >> 1;
s[q1].l = hebing(s[q1].l, s[q2].l, l, mid);
s[q1].r = hebing(s[q1].r, s[q2].r, mid + 1, r);
s[q1].siz = s[s[q1].l].siz + s[s[q1].r].siz;
return q1;
}
int query(int q, int l, int r, int k)//线段树上二分
{
if(s[q].siz < k || q == 0) return -1;
if(l == r) return s[q].id;
int mid = (l + r) >> 1;
if(k <= s[s[q].l].siz)
return query(s[q].l,l,mid,k);
return query(s[q].r,mid + 1,r,k - s[s[q].l].siz);
}
void shuchu(int q, int l, int r)
{
if(!q) return ;
if(l == r)
{
cout << l << ' ' << r << ' ' <<s[q].id << ' ' << s[q].siz << '\n';
return ;
}
int mid = (l + r) >> 1;
shuchu(s[q].l,l,mid);
shuchu(s[q].r,mid + 1,r);
}
void test()
{
for1(i,1,n)
{
int x = find(i);
printf("Now is %d in Tree No.%d : \n",i,x);
shuchu(rt[x],1,n);
cout <<"_____________\n";
}
cout << "======================\n\n";
}
}Tree;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
int x,y,k;
for1(i,1,n)
{
cin >> x;
fa[i] = i;
Tree.rt[i] = Tree.build(Tree.rt[i],1,n,x,i);
}
for1(i,1,m)
{
cin >> x >> y;
x = find(x);
y = find(y);
fa[y] = x;
Tree.rt[x] = Tree.hebing(Tree.rt[x],Tree.rt[y],1,n);
}
// Tree.test();
char ji;
cin >> q;
for1(i,1,q)
{
cin >> ji;
if(ji == 'B')
{
cin >> x >> y;
x = find(x);
y = find(y);
fa[y] = x;
Tree.rt[x] = Tree.hebing(Tree.rt[x],Tree.rt[y],1,n);
}
else
{
cin >> x >> k;
x = find(x);
cout << Tree.query(Tree.rt[x],1,n,k) << '\n';
}
// Tree.test();
}
return 0;
}
二维偏序 poj2353 Stars
树状数组应用(树状数组单独开了一个随笔)
#include<iostream>
#include<stdio.h>
#include<algorithm>
#define for1(i,a,b) for(int i = a;i<=b;i++)
#define ll long long
#define mp(a,b) make_pair(a,b)
using namespace std;
struct node{
int x;
int y;
}a[5000005];
int s[5000005],n,ans[5000005],m;
bool cmp(node x,node y)
{
if(x.y==y.y) return x.x<y.x;
return x.y<y.y;
}
int lb(int x)
{
return x&-x;
}
void xg(int x,int k)
{
while(x<=32005)
{
s[x]+=k;
x+=lb(x);
}
}
int cx(int x)
{
int ans=0;
while(x)
{
ans+=s[x];
x-=lb(x);
}
return ans;
}
int main()
{
cin>>n;
for1(i,1,n)
scanf("%d%d",&a[i].x,&a[i].y);
sort(a+1,a+n+1,cmp);
for1(i,1,n)
{
xg(a[i].x+1,1);
ans[cx(a[i].x+1)]++;
}
for1(i,1,n)
printf("%d\n",ans[i]);
return 0;
}
P5854 【模板】笛卡尔树
#include <bits/stdc++.h>
#define for1(i,a,b) for(int i = a;i<=b;i++)
using namespace std;
int zhan[50000005], top;
int n, p[50000005];
//节点的两个权值只需要储存一个,因为另一个就是它的下标
int dikaer[50000005][2];
//dicaer[i][0]表示左儿子,dicaer[i][1]表示节点i的右儿子
long long ji1, ji2; //不开longlong见祖宗
int read() {
int sum = 0, flag = 1;
char ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-')
ch = getchar();
if (ch == '-')
flag = -1, ch = getchar();
while (ch >= '0' && ch <= '9')
sum = sum * 10 + ch - '0', ch = getchar();
return sum * flag;
}
int main() {
n = read();
for1(i, 1, n) p[i] = read();
for1(i, 1, n) {
while (top && p[zhan[top]] > p[i])
dikaer[i][0] = zhan[top--];
//栈中存的是下标,如果此时插入的结点p[i]比栈顶的元素要小
//就需要将栈顶元素弹出,直到栈顶元素比它要小时
//最后一个弹出的节点就会变成新插入的节点的左儿子
dikaer[zhan[top]][1] = i;
//如果满足条件,那么新插入的节点就会是栈顶元素的儿子
//如果一开始满足栈顶元素比它要大,那么就不需要进入while循环
//但即使刚开始不满足,在进入while循环满足之后也需要进行满足时的操作
zhan[++top] = i;
//新元素入栈,由于有while循环,所以本质上这是一个单调栈
}
for1(i, 1, n)
ji1 ^= 1ll * i * (dikaer[i][0] + 1),//细节:1ll相当于将数据从int转化为了long long
ji2 ^= 1ll * i * (dikaer[i][1] + 1);
printf("%lld %lld\n", ji1, ji2);
return 0;
}
P3865 【模板】ST 表:
https://www.luogu.com.cn/blog/XTZORZ/solution-p3865
#include<bits/stdc++.h>
#define for1(i,a,b) for(int i = a;i<=b;i++)
using namespace std;
string a,b;
int lg[500005];//lg[i]意思为log 2 i 的整数部分
int st[500005][21];//指从第i个位置开始的往后2的 j 次方个数里的最大值(第i个数也算)比如st[i][0]=a[i];
int n,m;
int main()
{
cin>>n>>m;
for1(i,1,n)
scanf("%d",&st[i][0]);//2的 0 次方就是自己
for1(i,2,n+1) lg[i]=lg[i>>1]+1;
for1(j,1,lg[n])//有可能用到的最大的 j 次方
for(int i=1;i<=n-(1<<j)+1;i++)//这里的 i+(1<<j)-1其实就是整个这个 st 表包含数据的长度
st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
//如:
//i i+(1<<(j-1))-1 i+(1<<j)-1
//|————————————————--———|———————————————-----—————|
//从i往后数2的 j 次方的最大值=max(从i往后数2 的 j-1 次方的最大值,从i+(1<<(j-1))-1往后数2的 j-1 次方的最大值)
int x,y;
for(int i = 1;i <=m;i++)
{
scanf("%d%d",&x,&y);
int l = lg[y - x+1];
printf("%d\n",max(st[x][l],st[y-(1 << l) + 1][l]));
}
return 0;
}
平衡树Treap P3369【模板】普通平衡树
#include<bits/stdc++.h>
#define for1(i,a,b) for(int i = a;i<=b;i++)
using namespace std;
struct node{
int d;
int v;
int l;
int r;
int s;
}t[5000005];
int cnt;
int root;
void son(int x)//统计儿子个数
{
t[x].s=t[t[x].l].s+t[t[x].r].s+1;
}
// 4(num) 2
// / \ / \
// 2 5 --- 1 4(num)
// / \ / \
//1 3 3 5
//对于以v为根的子树进行Zig(右旋)操作,步骤如下:
//(1)假设v的左二子为c,调整v的左二子为c的右儿子。
//(2)调整c的右儿子为v。
//(3)调整根v为c。
void rxuan(int &now) //右旋
{
int ji=t[now].l;
t[now].l=t[ji].r;
t[ji].r=now;
t[ji].s=t[now].s;
son(now);
now=ji;
}
// 2(num) 4
// / \ / \
// 1 4 --- 2(num)5
// / \ / \
// 3 5 1 3
// 对于以v为根的子树进行Zag(左旋)操作,步骤如下:
//(1)假设v的右二子为c,调整v的右二子为c的左儿子。
//(2)调整c的左儿子为v。
//(3)调整根v为c。
void zxuan(int &now) //左旋
{
int ji=t[now].r;
t[now].r=t[ji].l;
t[ji].l=now;
t[ji].s=t[now].s;
son(now);
now=ji;
return ;
}
//给节点随机分配一个优先级(value),先把要插入的点插入到一个叶子上,
//然后跟维护堆一样,我们维护一个 小根堆,
//如果当前节点的优先级比它的儿子大就旋转,
//如果当前节点是根的左儿子就右旋,如果当前节点是根的右儿子就左旋
void charu(int &now,int x)
{
if(now==0)
{
now=++cnt;
t[now].s=1;
t[now].d=x;
t[now].v=rand()*rand()%19620817;
return ;
}
t[now].s++;
if(x>=t[now].d)
charu(t[now].r,x);
else
charu(t[now].l,x);
if(t[now].l!=0&&t[now].v>t[t[now].l].v)
rxuan(now);
if(t[now].r!=0&&t[now].v>t[t[now].r].v)
zxuan(now);
son(now);
}
//因为TreapTreap满足堆性质,所以只需要把要删除的节点旋转到叶节点上,然后直接删除就可以了。
//具体的方法:
//如果该节点的左子节点的优先级小于右子节点的优先级,
//右旋该节点,使该节点降为右子树的根节点,然后访问右子树的根节点,继续操作;
//反之,左旋该节点,使该节点降为左子树的根节点,然后访问左子树的根节点,
//继续操作,直到变成可以直接删除的节点。
void shanchu(int &now,int x)//删除
{
t[now].s--;
if(t[now].d==x)
{
if(t[now].l==0&&t[now].r==0)
{
now=0;
return ;
}
if(t[now].l==0||t[now].r==0)
{
now=t[now].l+t[now].r;
return ;
}
if(t[t[now].l].v<t[t[now].r].v)
{
rxuan(now);
shanchu(t[now].r,x);
return ;
}
else
{
zxuan(now);
shanchu(t[now].l,x);
return ;
}
}
if(t[now].d>=x)
shanchu(t[now].l,x);
else
shanchu(t[now].r,x);
son(now);
}
//显然,若t[now].data<data,则在now的右子树中仍有部分小于data的数,
//所以在加上t[t[now].left].siz+1的同时还需在now的右子树中继续递归。反之,则只需在左子树中递归
int ran(int now,int x)//查询排名
{
if(now==0)
return 0;
if(x>t[now].d)
return t[t[now].l].s+1+ran(t[now].r,x);
return ran(t[now].l,x);
}
//若左子树的大小刚好为x-1x?1,则当前节点的data即为所求结果。
//若左子树的大小大于x-1x?1,则在右子树中递归查找。
//若左子树的大小小于x-1x?1,则在左子树中递归查找
int find(int now,int x)//查询排名为k的数
{
if(x==t[t[now].l].s+1)
return t[now].d;
if(x>t[t[now].l].s+1)
return find(t[now].r,x-t[t[now].l].s-1);
return find(t[now].l,x);
}
//若now==0,则不存在返回值(return 0)。
//若当前节点的值大于等于data,则在右子树中找。
//若当前节点的值小于data,则在左子树中找,若找不到,则返回当前节点的值。
int qian(int now,int x)//查询前驱
{
if(now==0)
return 0;
if(t[now].d>=x)
return qian(t[now].l,x);
int ji=qian(t[now].r,x);
if(ji==0)
return t[now].d;
return ji;
}
int hou(int now,int x)//查询后继
{
if(now==0)
return 0;
if(t[now].d<=x)
return hou(t[now].r,x);
int ji=hou(t[now].l,x);
if(ji==0)
return t[now].d;
return ji;
}
int n,ji,x;
int main()
{
srand(19620817);
scanf("%d",&n);
for1(i,1,n)
{
scanf("%d %d",&ji,&x);
if(ji==1) charu(root,x);
if(ji==2) shanchu(root,x);
if(ji==3) printf("%d\n",ran(root,x)+1);
if(ji==4) printf("%d\n",find(root,x));
if(ji==5) printf("%d\n",qian(root,x));
if(ji==6) printf("%d\n",hou(root,x));
}
return 0;
}
P3369【模板】普通平衡树 替罪羊树
由于实在太长了,bug的找🤮了,所以代码和
https://blog.csdn.net/a_forever_dream/article/details/81984236
很相似。。。
#include <bits/stdc++.h>
#define for1(i,a,b) for(int i = a;i <= b;i ++)
using namespace std;
const int maxn = 1e6 + 11;
struct node
{
int zuo;
int you;
int x;
int tot;
int size;
int trsize;
int whsize;
int fa;
bool tag;
};
//size表示以该节点为根的子树内有多少个节点(包括删除的点),
//trsize表示有多少个有效节点(不包括删除的点),whsize表示有多少个数(tot之和)
node tree[maxn];
int len, n, root;
int ck[maxn], t;
double alpha = 0.75;
void build(int x,int y,int fa)
//在标号为y的节点上建一个新节点,权值为x,父亲是fa
{
tree[y].zuo = tree[y].you = 0;
tree[y].fa = fa;
tree[y].tag = false;
tree[y].x = x;
tree[y].tot = tree[y].size = tree[y].trsize = tree[y].whsize = 1;
}
int kk()
{
if(t > 0)
return ck[t --];
else
return ++ len;
}
void updata(int x, int y, int z, int k)
//上传到了节点x,三种size的值分别为y, z, k
{
if(!x)
return;
tree[x].trsize += y;
tree[x].size +=z;
tree[x].whsize += k;
updata(tree[x].fa, y, z, k);
}
int find(int x,int now)
//要找到值为x的点所在的编号,此时已经到了节点now(没有就返回空的),相当于二分
{
if(x < tree[now].x && tree[now].zuo)
return find(x, tree[now].zuo);
if(x > tree[now].x && tree[now].you)
return find(x, tree[now].you);
return now;
}
//倘若这个点正好没有想找的儿子,就直接返回空的编号,如果自己就是要找的点,就会直接返回
struct sl{
int x;
int tot;
}shulie[1000010];
int tt;
void dfs_rebuild(int x)//把以x为根的所有节点放进中序遍历的数列中
{
if(x==0)
return;
dfs_rebuild(tree[x].zuo);
if(!tree[x].tag)//被删除的点重构时就不加进去了
{
shulie[++ tt].x = tree[x].x;
shulie[tt].tot = tree[x].tot;
}
ck[++ t] = x;
dfs_rebuild(tree[x].you);
//相当是以中序遍历的方式把树存了起来
}
int readd(int l,int r,int fa)
//将中序遍历出的节点插回去
//显然把中位数当根是最平衡的
{
if(l>r)
return 0;
int mid=(l+r)>>1;
int id=kk();
tree[id].fa = fa;
tree[id].tot = shulie[mid].tot;
tree[id].x = shulie[mid].x;
tree[id].zuo = readd(l,mid-1,id);
tree[id].you = readd(mid+1,r,id);
tree[id].whsize = tree[tree[id].zuo].whsize+tree[tree[id].you].whsize+shulie[mid].tot;
//和线段树原理差不多
tree[id].size = tree[id].trsize = r-l+1;
//因为之前无效点都没加进数列,所以全都是有效节点
tree[id].tag = false;
return id;
}
void rebuild(int x)//重构以x为根节点的树
{
tt = 0;
dfs_rebuild(x);//抽出树的中序遍历数列
if(x == root)
root = readd(1, tt, 0);
//如果把整棵树都给重建了,那就得重新找根
else
{
updata(tree[x].fa, 0, -tree[x].size + tree[x].trsize, 0);
//相当于减去了所有的无效节点,然后上传
if(tree[tree[x].fa].zuo==x)//x是父亲的左儿子
tree[tree[x].fa].zuo = readd(1, tt, tree[x].fa);
else
tree[tree[x].fa].you = readd(1, tt, tree[x].fa);
}//找到原来的节点的父亲,让他连向重构之后的根节点
}
void find_rebuild(int now,int x)
//从节点x开始向下找权值为x的节点,看看他们是否可以重构
{
if((double)tree[tree[now].zuo].size>(double)tree[now].size*alpha||
(double)tree[tree[now].you].size>(double)tree[now].size*alpha||
//如果左右子树的占比超过了 (100 * alpha) %
(double)tree[now].size-(double)tree[now].trsize>(double)tree[now].size*0.4)
//或者无效节点超过了 40%
{
rebuild(now);
return;
}
if(tree[now].x != x)//没找到就继续向下走
find_rebuild(x<tree[now].x?tree[now].zuo:tree[now].you,x);
}
void add(int x)//插入点
{
if(root == 0)
{
build(x,root = kk(),0);
return;
}
int p = find(x,root);
if(x == tree[p].x)//该节点存在
{
tree[p].tot ++;
if(tree[p].tag)//如果该点是被删掉的就再加回来
{
tree[p].tag = false;
updata(p,1,0,1);
}
else
updata(p,0,0,1);
}
else if(x<tree[p].x) //节点不存在,需要自己建
{
build(x,tree[p].zuo=kk(),p);
updata(p,1,1,1);
}
else
{
build(x,tree[p].you=kk(),p);
updata(p,1,1,1);
}
find_rebuild(root,x);
}
void del(int x)//删点
{
int p=find(x,root);
tree[p].tot --;
if(!tree[p].tot)
{
tree[p].tag = true;
updata(p, -1, 0, -1);
}
else
updata(p, 0, 0, -1);
find_rebuild(root,x);
}
void findxpm(int x)//找x的排名
{
int now = root;
int ans = 0;
while(tree[now].x != x)//一层一层跳,最多只有logn层
{
if(x < tree[now].x)
now = tree[now].zuo;
else
{
ans += tree[tree[now].zuo].whsize + tree[now].tot;
//向右跳需要加上左边的数的个数
now = tree[now].you;
}
}
ans += tree[tree[now].zuo].whsize;
printf("%d\n",ans+1);
}
void findpmx(int x)//找到排名为x的数
{
int now = root;
while(1)
{
if(x <= tree[tree[now].zuo].whsize)
now = tree[now].zuo;
else
{
x -= tree[tree[now].zuo].whsize;
if(x <= tree[now].tot)
{
printf("%d\n",tree[now].x);
return;
}
x -= tree[now].tot;
now = tree[now].you;
}
}
}
bool ans;
void dfs_rml(int x)//辅助找前前驱
{
if(tree[x].you != 0)
dfs_rml(tree[x].you);
if(ans)
return;
if(! tree[x].tag)
{
printf("%d\n",tree[x].x);
ans = true;
return;
}
if(tree[x].zuo != 0)
dfs_rml(tree[x].zuo);
}
void dfs_lmr(int x)//辅助找后继
{
if(tree[x].zuo!=0)
dfs_lmr(tree[x].zuo);
if(ans)
return;
if(!tree[x].tag)
{
printf("%d\n",tree[x].x);
ans = true;
return;
}
if(tree[x].you!=0)dfs_lmr(tree[x].you);
}
void pre(int now,int x,bool zy)
//先找到值为x的节点,然后看看有没有左儿子,
//如果有,就将左子树遍历一遍,顺序是:右儿子->根->左儿子,
//找到的第一个没有被删除的节点就是答案。
{
if(!zy)
{
pre(tree[now].fa, x, tree[tree[now].fa].you == now);
return;
}
if(! tree[now].tag && tree[now].x < x)
{
printf("%d\n", tree[now].x);
return;
}
if(tree[now].zuo)
{
ans = false;
dfs_rml(tree[now].zuo);
return;
}
pre(tree[now].fa, x, tree[tree[now].fa].you == now);
}
void nxt(int now, int x, bool zy)
//相当于和前驱反过来
{
if(!zy)
{
nxt(tree[now].fa, x, tree[tree[now].fa].you != now);
return;
}
if(! tree[now].tag && tree[now].x > x)
{
printf("%d\n",tree[now].x);
return;
}
if(tree[now].you)
{
ans = false;
dfs_lmr(tree[now].you);
return;
}
nxt(tree[now].fa, x, tree[tree[now].fa].you != now);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
while(n--)
{
int id,x;
cin >> id >> x;
if(id == 1) add(x);
if(id==2) del(x);
if(id==3) findxpm(x);
if(id==4) findpmx(x);
if(id==5) pre(find(x,root),x,true);
if(id==6) nxt(find(x,root),x,true);
}
}
莫队 P1494 [国家集训队] 小 Z 的袜子
#include<bits/stdc++.h>
#define for1(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
using namespace std;
struct node{//询问
int l;
int r;
int id;
ll fz,fm;
}a[500005];
int n,m,c[500005],ji[500005];
int pos[50005];//记录每个数据属于哪个块
int l,r;
ll ans;//记录当前答案
ll gcd(ll x,ll y)
{
return y==0?x:gcd(y,x%y);
}
bool cmp(node x,node y)
{
if(pos[x.l]==pos[y.l]) return x.r<y.r;
return x.l<y.l;
}
bool cmp2(node x,node y)
{
return x.id<y.id;
}
void jia(int x,int y)//需要添加、删除的元素下标,区间长度的变化
{
ans-=ji[c[x]]*ji[c[x]];//减去原来的区间和
ji[c[x]]+=y;//添加、删除元素
ans+=ji[c[x]]*ji[c[x]];//更新
}
void cl() //处理询问
{
for(int l=1,r=0,i=1;i<=m;i++)
{
while(r<a[i].r)
{
jia(r+1,1);
r++;
}
while(r>a[i].r)
{
jia(r,-1);
r--;
}
while(l<a[i].l)
{
jia(l,-1);
l++;
}
while(l>a[i].l)
{
jia(l-1,1);
l--;
}
if(a[i].l==a[i].r)//特判
{
a[i].fz=0;
a[i].fm=1;
continue;
}
a[i].fz=ans-(a[i].r-a[i].l+1);//平方和减区间长度
a[i].fm=(a[i].r-a[i].l+1)*1ll*(a[i].r-a[i].l);
ll g=gcd(a[i].fz,a[i].fm);//约分
a[i].fz/=g;
a[i].fm/=g;
}
}
int main()
{
scanf("%d%d",&n,&m);
for1(i,1,n) scanf("%d",&c[i]);
int shuliang=sqrt(n);//分块的数量
for1(i,1,n)
pos[i]=(i-1)/shuliang+1;
for1(i,1,m)
{
scanf("%d%d",&a[i].l,&a[i].r);
a[i].id=i;
}
sort(a+1,a+m+1,cmp);//排序
cl();//处理
sort(a+1,a+m+1,cmp2);//恢复原来的顺序
for1(i,1,m)
printf("%lld/%lld\n",a[i].fz,a[i].fm);
return 0;
}
P3812 【模板】线性基
线性基并不是只有模板上的那些作用
更多参考:https://www.luogu.com.cn/blog/szxkk/solution-p3812
线性基我也不知道应该算是数学还是数据结构,所以就都放了
#include <bits/stdc++.h>
#define for1(i,a,b) for(register ll i = a;i <= b;i ++)
#define ll long long
using namespace std;
const ll maxn = 2020;
const ll mod = 1e9 + 7;
int n, m;
struct node{
ll p[64];
ll d[64];
ll cnt;
node()
{
memset(p,0,sizeof(p));
cnt = 0;
}
void Rebuild()
{
cnt = 0;
for(ll i = 63;i >= 0 ;i --)
for(int j = i - 1;j >= 0;j --)
if(p[i] & (1ll << j))
p[i] ^= p[j];
for1(i,0,63)
if(p[i])
d[cnt ++] = p[i];
}
ll Kth(ll k)//求能表示出来的第k大
{
if(k >= (1ll << cnt))
return -1;
ll ans = 0;
for(ll i = 63;i >= 0;i--)
if(k & (1ll << i))
ans ^= p[i];
return ans;
}
void Insert(ll x)
{
for(ll i = 63;i >= 0;i--)
if(x & (1ll << i))
{
if(!p[i])
{
p[i] = x;
cnt ++;
break;
}
else
x ^= p[i];
}
return ;
}
ll FindMax()
{
ll ans = 0;
for(ll i = 63;i >= 0;i--)
{
if((ans ^ p[i]) > ans)
{
ans ^= p[i];
}
}
return ans;
}
}xian;
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(nullptr);
cin >> n;
ll x;
for1(i,1,n)
cin >> x , xian.Insert(x);
cout << xian.FindMax() << '\n';
return 0;
}

浙公网安备 33010602011771号