数据结构

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

image
模板题所在的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;
}
posted @ 2022-02-17 11:37  yyx525jia  阅读(14)  评论(0)    收藏  举报