【算法】数据结构模板整理

151 阅读24分钟

整理一些竞赛常用的数据结构和字符串算法模板。

STL

bitset

bitset<4> bs;
cout<<bs<<endl;

// bitset对于二进制有位运算符,可以通过[]运算符访问元素

count()     // 求bitset中1的数量
size()      // 求bitset的大小
test(int a) // 求下标a是0还是1
any()  // 检查是否存在1
none() // 检查是否不存在1
all()  // 检查是否全部为1

filp(int a) // 翻转下标a
filp()      // 翻转全部
set()       // 全部置为1
set(int a,int b=1) // 将位置a设置为b
reset(int a)       // 将位置a设为0
reset()            // 将每一位设置为0

pb-ds rbtree

优点是代码量少,缺点是常数极大。

#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
typedef tree<pt, null_type, less<pt>, rb_tree_tag, tree_order_statistics_node_update> rbtree;

/*
定义一颗红黑树
int 关键字类型
null_type无映射(低版本g++为null_mapped_type)
less<int>从小到大排序
rb_tree_tag 红黑树(splay_tree_tag)
tree_order_statistics_node_update结点更新
插入t.insert();
删除t.erase();
Rank:t.order_of_key();
第K值:t.find_by_order();
前驱:t.lower_bound();
后继t.upper_bound();
a.join(b)b并入a 前提是两棵树的key的取值范围不相交
a.split(v, b)key小于等于v的元素属于a,其余的属于b
T.lower_bound(x)   >=x的min的元素迭代器
T.upper_bound(x)  >x的min的元素迭代器
T.find_by_order(k) 有k个数比它小的数
*/

struct pt {
    int first, second;
    pt(int x, int y) :first(x), second(y) {}
    bool operator < (const pt h) const {
        return first < h.first || (first == h.first && second < h.second);
    }
    bool operator == (const pt h) const {
        return first == h.first && second == h.second;
    }
};

pb_ds priority_queue

pb_ds库的push操作是有返回值的(与STL不同),返回的类型就是迭代器, 这样用一个迭代器数组保存所有push进优先队列的元素的迭代器,就可以随时修改优先队列内部元素了。

#include<ext/pb_ds/priority_queue.hpp>
typedef __gnu_pbds::priority_queue<node, less<node>, pairing_heap_tag> heap;
heap::point_iterator hit[M];
heap pq;

pq.modify(hit[e[i].to], node(e[i].to,d[e[i].to]));
a.join(b);
此时优先队列b内所有元素就被合并进优先队列a中,且优先队列b被清空。
hit[i]=pq.push(i);
if(hit[i]==0){
    pq.modify(hit[i],1);
}
pq.erase(hit[i]);

rope

二叉树实现,存储字符串,时间复杂度O(logN)O(logN)。 2018牛客多校。序列翻转。

#include <ext/rope>

using namespace __gnu_cxx;
rope<int> T;
for(int i=1;i<=n;i++) T.push_back(i);
T = T.substr(p,s) + T.substr(0,p)+T.substr(p+s,n-p-s);
for(int i=1;i<=n;i++) printf("%d ",T.at(i));
可持久化:
rope<char> *his[maxn];
his[0] = new rope<char>();
his[i] = new rope<char>(*his[i-1]);
O(1)copy历史版本
insert(位置,值)
erase(位置,大小)
substr(位置,大小)
test.push_back(x);//在末尾添加x
test.insert(pos,x);//在pos插入x  
test.erase(pos,x);//从pos开始删除x个
test.copy(pos,len,x);//从pos开始到pos+len为止用x代替
test.replace(pos,x);//从pos开始换成x
test.substr(pos,x);//提取pos开始x个
test.at(x)/[x];//访问第x个元素

list

百度之星。合并两个链表。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
list<int> mp[300005];
int main(){
    //freopen("a.in","r",stdin);
    int t;scanf("%d",&t);
    while(t--){
        int n,q;scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++){
            mp[i].clear();
        }
        int op,a,b;
        while(q--){
            scanf("%d",&op);
            if(op==1){
                scanf("%d%d",&a,&b);
                mp[a].push_back(b);
            }
            else if(op==2){
                scanf("%d",&a);
                if(mp[a].empty()){
                    puts("EMPTY");
                }
                else{
                    int v=mp[a].back();
                    mp[a].pop_back();
                    printf("%d\n",v);
                }
            }
            else{
                scanf("%d%d",&a,&b);
                mp[a].splice(mp[a].end(),mp[b]);
            }
        }
        for(int i=1;i<=n;i++){
            mp[i].clear();
        }
    }
    return 0;
}

nth_element

2018牛客多校第六场。O(n)O(n)

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
unsigned n, A, B, C;
unsigned x = A, y = B, z = C;
unsigned tang(){
    unsigned t;
    x ^= x << 16;x ^= x >> 5;x ^= x << 1;t = x;x = y;y = z;z = t ^ x ^ y;
    return z;
}
ull a[10000007];
int main(){
    int T;
    scanf("%d", &T);
    for(int ca = 1; ca <= T; ++ca) {
        int l=1,r=0;
        scanf("%u%u%u%u", &n, &A, &B, &C);
        x = A, y = B, z = C;
        for(int i=1;i<=n;i++){
            a[i]=tang();
        }
        int m=min(100,(int)n);
        ull ans=0;
        nth_element(a+1,a+1+n-m,a+1+n);
        for(int i=n-m+1;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                ans=max(ans,a[i]*a[j]/__gcd(a[i],a[j]));
            }
        }
        printf("Case #%d: %llu\n", ca, ans);
    }
    return 0;
}

指针建树

二叉搜索树指针版

#include <bits/stdc++.h>
using namespace std;
struct node{
    int v;
    node *l,*r;
    node():l(NULL),r(NULL){};
    node(int v):v(v),l(NULL),r(NULL){};
};
void build(node* u,int val){
    if(val < u->v){
        if(u->l!=NULL)build(u->l,val);
        else u->l = new node(val);
    }else{
        if(u->r!=NULL)build(u->r,val);
        else u->r = new node(val);
    }
}
int main(){
    int t;cin>>t;
    while(t--){
        int n;cin>>n;n--;
        int x;cin>>x;
        node* root = new node();
        root->v = x;
        while(n--){
            cin>>x;
            build(root,x);
        }
    }
    return 0;
}

指针动态开点trie

using namespace std;
#define ll long long
int t,n,a,ok;
char s[100];
struct trie{
    trie *child[11];
    bool qwq;
    trie(){
        qwq=false;
        memset(child,0,sizeof(child));
    }
};
trie *root,*now,*next1;
void insert(char *s){
    now=root;
    int len = strlen(s);
    for(int i=0;i<len;i++){
        if(now->qwq==true)ok=0;
        int m=s[i]-'0';
        if(now->child[m]!=NULL) now = now->child[m];
        else {
            next1 = new trie;
            now->child[m] = next1;
            now = next1;
        }
        if(i==len-1)  now->qwq=true;
    }
}
void removetrie(trie* u){
    if(u==NULL) return ;
    for(int i=0;i<10;i++)removetrie(u->child[i]);
    delete u;
}
int main(){
    cin>>t;
    while(t--){
        root = new trie;
        ok=1;
        cin>>n;
        for(int i=0;i<n;i++){
            scanf("%s",s);
            insert(s);
        }
        cout<<((ok)?"YES\n":"NO\n");
        removetrie(root);
    }
    return 0;
}

指针静态开点trie

int t,n,a,ok;
int cnt;
char s[100];
struct trie{
    trie *child[11];
    bool qwq;
    void init(){
        qwq=false;
        memset(child,0,sizeof(child));
    }
}p[100005];
inline trie* newtrie(){
    p[cnt].init();
    return &p[cnt++];
}
trie *root,*now,*next1;
void insert(char *s){
    now = root;
    next1 = NULL;
    int len = strlen(s);
    for(int i=0;i<len && ok;i++){
        if(now->qwq==true){ok=0;return;}
        int m=s[i]-'0';
        if(now->child[m]!=NULL)  now = now->child[m];
        else{
            next1 = newtrie();
            now->child[m] = next1;
            now = next1;
        }
        if(i==len-1)now->qwq=true;
    }
}
int main(){
    cin>>t;
    while(t--){
        cnt=0;
        root = newtrie();
        ok=1;
        cin>>n;
        for(int i=0;i<n;i++){
            scanf("%s",s);
            if(ok)insert(s);
        }
        cout<<((ok)?"YES\n":"NO\n");
    }
    return 0;
}

单调队列、单调栈

2019出题,利用单调性求出贡献区间。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int mod = 1e9+7;
const int maxn = 2e6+10;
int n;
int a[maxn];
int len1[maxn],len2[maxn];
int main(){
    freopen("std.in","r",stdin);
    freopen("stdn.out","w",stdout);
    int t;cin>>t;
    vector<pair<int,int> >vec;
    while(t--){
        while(vec.empty()==0)vec.pop_back();
        vec.push_back(make_pair(-1,0));
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",a+i);
        ll ans=0;
        for(int i=1;i<=n;i++){
            while(vec.back().first>a[i])vec.pop_back();
            len1[i]=i-vec.back().second;
            vec.push_back(make_pair(a[i],i));
        }
        while(vec.empty()==0)vec.pop_back();
        vec.push_back(make_pair(-1,n+1));
        for(int i=n;i>=1;i--){
            while(vec.back().first>=a[i])vec.pop_back();
            len2[i]=vec.back().second-i;
            vec.push_back(make_pair(a[i],i));
            ans+=1ll*len1[i]*len2[i]*a[i];
            ans%=mod;
        }
        cout<<ans<<endl;
    }
    return 0;
}

树状数组

一维树状数组基础模板

#include<bits/stdc++.h>
using namespace std;
#define maxn 1000060
int a[maxn],c[maxn];
int lowbit(int x){return x&(-x);}
int n;
int sum(int i){
    int s=0;
    while(i){
        s+=c[i];
        i-=lowbit(i);}
    return s;
}
void add(int i,int v){
    while(i<=n){
        c[i]+=v;
        i+=lowbit(i);
    }
}
int main(){
    int a,b;
    while(scanf("%d",&n)!=EOF,n){
        memset(c,0,sizeof(c));
        for(int i=0;i<n;i++){
            scanf("%d%d",&a,&b);
            add(a,1);add(b+1,-1);
        }
        for(int i=1;i<n;i++)
            printf("%d ",sum(i));
        printf("%d\n",sum(n));
    }
    return 0;
}

树状数组-全局第k大和排名

#include<bits/stdc++.h>
const int maxn=1<<25;
const int p=1e7+10;
int a[maxn+1];
inline void add(int x,int v){x+=p;while(x<=maxn)a[x]+=v,x+=x&-x;}
inline int query(int x){x+=p;int ans=0;while(x)ans+=a[x],x&=x-1;return ans;}
inline int kth(int k,int rt=maxn){
    for(int i=rt;i>>=1;)
        if(a[rt-i]>=k)rt-=i;
        else k-=a[rt-i];
    return rt-p;
}
int n,opt,x;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%d%d",&opt,&x);
        if(opt==1)add(x,1);
        if(opt==2)add(x,-1);
        if(opt==3)printf("%d\n",query(x-1)+1);
        if(opt==4)printf("%d\n",kth(x));
        if(opt==5)printf("%d\n",kth(query(x-1)));
        if(opt==6)printf("%d\n",kth(query(x)+1));
    }
}

树状数组离线求区间不同数字个数

2018牛客多校第一场。

#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x&-x)
#define max(a,b) ((a)>(b)?(a):(b))
const int N=200005, M=200005;
int bit[N], a[N], n, m, ihead[100005], inext[N];
struct Q {
    int l, r, id, ans;
}q[M];
bool cmp1(const Q &a, const Q &b) { return a.l==b.l?a.r<b.r:a.l<b.l; }
bool cmp2(const Q &a, const Q &b) { return a.id<b.id; }
inline int read() {
    int ret=0; char c;
    for(c=getchar(); c<'0' || c>'9'; c=getchar());
    for(; c>='0' && c<='9'; c=getchar()) ret=ret*10+c-'0';
    return ret;
}
inline void add(int x, const int &y) { while(x<=n) bit[x]+=y, x+=lowbit(x); }
inline int sum(int x) { int ret=0; while(x>0) ret+=bit[x], x-=lowbit(x); return ret; }
int main() {
    while(scanf("%d%d",&n,&m)!=EOF){
        memset(bit,0,sizeof bit);
        memset(ihead,0,sizeof ihead);
        memset(inext,0,sizeof inext);
        int i, maxi=0, l=1;
        for(i=1; i<=n; ++i){
            a[i]=read(), maxi=max(maxi, a[i]);
            a[i+n]=a[i];
        }
        n*=2;
        for(int i=1;i<=n;i++){
            maxi=max(maxi,a[i]);
        }
        for(i=n; i>=0; --i){
            inext[i]=ihead[a[i]], ihead[a[i]]=i;
        }
        for(i=0; i<=maxi; ++i){
            if(ihead[i]) add(ihead[i], 1);
        for(i=1; i<=m; ++i){
            q[i].l=read(), q[i].r=read(), q[i].id=i;
            int tmp=q[i].r;
            q[i].r=n/2+q[i].l;
            q[i].l=tmp;
        }
        sort(q+1, q+1+m, cmp1);
        for(i=1; i<=m; ++i) {
            while(l<q[i].l) {
                if(inext[l]) add(inext[l], 1);
                ++l;
            }
            q[i].ans=sum(q[i].r)-sum(q[i].l-1);
        }
        sort(q+1, q+1+m, cmp2);
        for(i=1; i<=m; ++i) printf("%d\n", q[i].ans);
    }
    return 0;
}

二维树状数组模板

using namespace std;
#define maxn 2005
#define lowbit(i) ((i)&(-i))
#define r(x) scanf("%d",&x)
int op,n,x,y,k;
int c[maxn][maxn];
int sum(int x,int y){
    int s=0;
    for(int i=x;i;i-=lowbit(i)){
        for(int j=y;j;j-=lowbit(j)){
            s+=c[i][j];
        }
    }return s;
}
void add(int x,int y,int v){
    for(int i=x;i<=n;i+=lowbit(i)){
        for(int j=y;j<=n;j+=lowbit(j)){
            c[i][j]+=v;
        }
    }
}
char ch;
int main(){
    int x1,x2,y1,y2;
    int t,q;
    r(t);
    while(t--){
        memset(c,0,sizeof(c));
        r(n);r(q);
        for(int i=0;i<q;i++){
            cin>>ch;
            if(ch=='C'){
                r(x1);r(y1);r(x2);r(y2);
                add(x1,y1,1);
                add(x1,y2+1,1);
                add(x2+1,y1,1);
                add(x2+1,y2+1,1);
            }
            else{
                r(x1);r(y1);
                cout<<((sum(x1,y1)&1)?1:0)<<endl;
            }
        }
        cout<<endl;
    }
    return 0;
}

线段树

区间修改,区间求和模板

using namespace std;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define intmid  int mid = (l+r)>>1
#define maxn 100555
typedef long long ll;
int n,q;
ll sum[maxn<<2],cur[maxn<<2];
void push_up(int rt){
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void push_down(int l,int r,int rt){
    if(cur[rt]){
        intmid;
        cur[rt<<1]  += cur[rt];
        cur[rt<<1|1]+= cur[rt];
        sum[rt<<1]  += (mid-l+1)*cur[rt];
        sum[rt<<1|1]+= (r-mid)*cur[rt];
        cur[rt]=0;
    }
}
void build(int l,int r,int rt){
    intmid;cur[rt]=0;
    if(l==r){
        scanf("%lld",&sum[rt]);return;
    }build(lson);build(rson);push_up(rt);
}
void update(int ll,int rr,int val,int l,int r,int rt){
    intmid;
    if( ll<=l && rr>=r ){
        cur[rt]+=val;
        sum[rt]+=(r-l+1)*val;
        return ;
    }
    push_down(l,r,rt);
    if(ll<=mid)update(ll,rr,val,lson);
    if(rr>mid)update(ll,rr,val,rson);
    push_up(rt);
}
ll query(int ll,int rr,int l,int r,int rt){
    long long ret=0;
    if(ll<=l && rr>=r) return sum[rt];
    push_down(l,r,rt);
    intmid;
    if(ll<=mid) ret += query(ll,rr,lson);
    if(rr>mid) ret += query(ll,rr,rson);
    return ret;
}
int main(){
    cin>>n>>q;
    build(1,n,1);
    char s[2];
    int a,b,c;
    while(q--){
        scanf("%s%d%d",s,&a,&b);
        if(s[0]=='C'){
            scanf("%d",&c);
            update(a,b,c,1,n,1);
        }
        else printf("%lld\n",query(a,b,1,n,1));
    }
    return 0;
}

标记永久化

inline int cross(int L,int R,int l,int r){
    L=max(L,l);R=min(R,r);return max(0,R-L+1);
}
void add(int L,int R,int l,int r,int rt){
    c[rt]+=cross(L,R,l,r);
    if(L<=l&&R>=r){
        cur[rt]++;return;
    }
    int mid = l+r>>1;
    if(!ls[rt])ls[rt]=++tot;
    if(!rs[rt])rs[rt]=++tot;
    if(L<=mid)add(L,R,l,mid,ls[rt]);
    if(R>mid) add(L,R,mid+1,r,rs[rt]);
}
ll ask(int L,int R,int l,int r,int rt,ll ad){
    if(L<=l&&R>=r)return c[rt] + ad*(r-l+1);
    int mid = l+r>>1;ll ret=0;
    if(L<=mid)ret+=ask(L,R,l,mid,ls[rt],ad + cur[rt]);
    if(R>mid) ret+=ask(L,R,mid+1,r,rs[rt],ad + cur[rt]);
    return ret;
}

and or max

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
int n,m;
int a[maxn];
const int INF = (1<<20)-1;
int maxv[maxn<<2],And[maxn<<2],Or[maxn<<2];/*区间最大值,与标记,或标记*/
int Andv[maxn<<2],orv[maxn<<2];/*区间与值,区间或值*/
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
void push_up(int rt){
    Andv[rt]=Andv[rt<<1]&Andv[rt<<1|1];
    orv[rt]=orv[rt<<1]|orv[rt<<1|1];
    maxv[rt]=max(maxv[rt<<1],maxv[rt<<1|1]);
}
void work_And(int rt,int v){
    maxv[rt]&=v;And[rt]&=v;Or[rt]&=v;
    Andv[rt]&=v;orv[rt]&=v;
}
void work_or(int rt,int v){
    maxv[rt]|=v;And[rt]|=v;Or[rt]|=v;
    Andv[rt]|=v;orv[rt]|=v;
}
void push_down(int rt){
    if(And[rt]!=INF){
        work_And(rt<<1,And[rt]);
        work_And(rt<<1|1,And[rt]);
        And[rt]=INF;
    }
    if(Or[rt]){
        work_or(rt<<1,Or[rt]);
        work_or(rt<<1|1,Or[rt]);
        Or[rt]=0;
    }
}
void build(int l,int r,int rt){
    And[rt]=INF,Or[rt]=0;
    if(l==r){
        maxv[rt]=Andv[rt]=orv[rt]=a[l];return;
    }int mid = l+r>>1;
    build(lson);build(rson);
    push_up(rt);
}
void update(int L,int R,int v,int op,int l,int r,int rt){
    if(L<=l&&R>=r){
        if(op==1){
            if(((v^INF)&((orv[rt]^INF)|Andv[rt]))==(v^INF)){
                work_And(rt,v);return;
            }
        }
        else{
            if((v&((orv[rt]^INF)|Andv[rt]))==v){
                work_or(rt,v);return;
            }
        }
    }
    push_down(rt);
    int mid = l+r>>1;
    if(L<=mid)update(L,R,v,op,lson);
    if(R>mid) update(L,R,v,op,rson);
    push_up(rt);
}
int query(int L,int R,int l,int r,int rt){
    if(L<=l&&R>=r)return maxv[rt];
    push_down(rt);
    int mid = l+r>>1,ret=0;
    if(L<=mid)ret=max(ret,query(L,R,lson));
    if(R>mid) ret=max(ret,query(L,R,rson));
    return ret;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)scanf("%d",a+i);
    build(1,n,1);
    int l,r,x,op;
    while(m--){
        scanf("%d%d%d",&op,&l,&r);
        if(op==3)printf("%d\n",query(l,r,1,n,1));
        else{
            scanf("%d",&x);
            update(l,r,x,op,1,n,1);
        }
    }
    return 0;
}

二维线段树模板

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int maxn = 1e4+10;
int sum[maxn][maxn<<2];
char op[10];
int h;
double a,l;
int h1,h2;
double a1,a2;
void push_up(int rtx,int rty){
    sum[rtx][rty]=max(sum[rtx][rty<<1],sum[rtx][rty<<1|1]);
}
void buildy(int rtx,int l,int r,int rty){
    sum[rtx][rty]=-1;
    if(l==r)return;
    int mid=l+r>>1;
    buildy(rtx,l,mid,rty<<1);buildy(rtx,mid+1,r,rty<<1|1);
}
void build(int l,int r,int rtx){
    buildy(rtx,0,1000,1);
    int mid=l+r>>1;
    if(l!=r){
        build(l,mid,rtx<<1);build(mid+1,r,rtx<<1|1);
    }
}
void updatey(int rtx,int p,int v,int l,int r,int rty){
    if(l==r){
        sum[rtx][rty]=max(sum[rtx][rty],v);
        return;
    }
    int mid=l+r>>1;
    if(p<=mid)updatey(rtx,p,v,l,mid,rty<<1);
    else      updatey(rtx,p,v,mid+1,r,rty<<1|1);
    push_up(rtx,rty);
}
void update(int x,int y,int v,int l,int r,int rtx){
    updatey(rtx,y,v,0,1000,1);
    if(l!=r){
        int mid=l+r>>1;
        if(x<=mid)update(x,y,v,l,mid,rtx<<1);
        else      update(x,y,v,mid+1,r,rtx<<1|1);
    }
}
int queryy(int rtx,int L,int R,int l,int r,int rty){
    if(L<=l&&R>=r){
            return sum[rtx][rty];
    }
    int mid=l+r>>1;
    int res=-1;
    if(L<=mid)res=max(res,queryy(rtx,L,R,l,mid,rty<<1));
    if(R>mid)res=max(res,queryy(rtx,L,R,mid+1,r,rty<<1|1));
    return res;
}
int query(int Lx,int Rx,int Ly,int Ry,int l,int r,int rtx){
    if(Lx<=l&&Rx>=r)return queryy(rtx,Ly,Ry,0,1000,1);
    int mid=l+r>>1;
    int res=-1;
    if(Lx<=mid)res=max(res,query(Lx,Rx,Ly,Ry,l,mid,rtx<<1));
    if(Rx>mid)res=max(res,query(Lx,Rx,Ly,Ry,mid+1,r,rtx<<1|1));
    return res;
}
int main(){
    int m;
    while(scanf("%d",&m)!=EOF,m){
        while(m--){
            build(100,200,1);
            scanf("%s",op);
            if(op[0]=='I'){
                scanf("%d%lf%lf",&h,&a,&l);
                update(h,a*10,l*10,100,200,1);
            }
            else{
                scanf("%d%d%lf%lf",&h1,&h2,&a1,&a2);
                if(h1>h2)swap(h1,h2);
                if(a1>a2)swap(a1,a2);
                int ans=query(h1,h2,a1*10,a2*10,100,200,1);
                if(ans==-1)puts("-1");
                else printf("%.1f\n",0.1*ans);
            }
        }
    }
    return 0;
}

权值线段树模板

// luogu-judger-enable-o2
/******权值线段树模板******/
/**
权值线段树维护全局值域信息,每个结点记录值域的值出现次数
如果值域太大,需要离线操作
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn =  1e5+10;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define intmid int mid = (l+r)>>1

ll t[maxn*4+1];
ll dt[maxn],tot;
ll hs(int x){return lower_bound(dt+1,dt+1+tot,x)-dt;}

void update(int p,int v,int l,int r,int rt){
    //单点修改 所到的值域都需要修改 复杂度logn
    t[rt]+=v;
    if(l==r)return ;
    intmid;
    if(p<=mid)update(p,v,lson);
    else update(p,v,rson);
}

int kth(int k,int l,int r,int rt){
    //如果左边多于k个,那么去左边找,否则找到右边
    if(l==r)return l;
    intmid;
    if(t[rt<<1]>=k)return kth(k,lson);
    return kth(k-t[rt<<1],rson);
}

int Rank(int p,int l,int r,int rt){
    //查询小于p的数出现的总次数,即区间[1,p-1]的数字个数,即rank
    if(l==r)return 1;
    intmid;
    if(p<=mid)return Rank(p,lson);
    return t[rt<<1]+Rank(p,rson);
}
struct node{
    ll first,second;
}p[maxn];
signed main(){
    int n;cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%lld%lld",&p[i].first,&p[i].second);
        if(p[i].first!=4)dt[++tot]=p[i].second;
    }
    sort(dt+1,dt+1+tot);
    tot=unique(dt+1,dt+1+tot)-dt-1;
    for(int i=1;i<=n;i++){
        if(p[i].first==1)update(hs(p[i].second),1,1,tot,1);
        else if(p[i].first==2)update(hs(p[i].second),-1,1,tot,1);
        else if(p[i].first==3)printf("%lld\n",Rank(hs(p[i].second),1,tot,1));
        else if(p[i].first==4)printf("%lld\n",dt[kth(p[i].second,1,tot,1)]);
        else if(p[i].first==5)printf("%lld\n",dt[kth(Rank(hs(p[i].second),1,tot,1)-1,1,tot,1)] );
        else if(p[i].first==6)printf("%lld\n",dt[kth(Rank(hs(p[i].second)+1,1,tot,1),1,tot,1)]);
    }
    return 0;
}

动态开点线段树

2019山东省赛。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
int n;
pair<int,int> p[maxn];
bool cmp(pair<int,int >a,pair<int,int> b){
    if(a.second==b.second)return a.first<b.first;
    return a.second<b.second;
}
struct node{
    int l,r,v,ls,rs;
    node(int l=0,int r=0,int v=0,int ls=0,int rs=0):l(l),r(r),v(v),ls(ls),rs(rs){}
}seg[maxn<<2];
int cnt,ans;
int f;
void solve(int L,int R,int rt){
    if(L<=seg[rt].l && R>=seg[rt].r){
        if(seg[rt].r-seg[rt].l==0){seg[rt].v=1;f=1;ans++;return;}
        if(!seg[rt].ls){
            seg[rt].ls=++cnt;
            seg[cnt]=node(seg[rt].l,seg[rt].l+seg[rt].r>>1);
        }
        if(seg[seg[rt].ls].r - seg[seg[rt].ls].l + 1 > seg[seg[rt].ls].v)solve(L,R,seg[rt].ls);
        else{
            if(!seg[rt].rs){
                seg[rt].rs=++cnt;
                seg[cnt]=node((seg[rt].l+seg[rt].r)/2+1,seg[rt].r);
            }
            if(seg[seg[rt].rs].r - seg[seg[rt].rs].l + 1 > seg[seg[rt].rs].v)solve(L,R,seg[rt].rs);
        }
        seg[rt].v=seg[seg[rt].ls].v + seg[seg[rt].rs].v;
        return;
    }
    seg[rt].v=seg[seg[rt].ls].v + seg[seg[rt].rs].v;if(f)return;
    if(L<=(seg[rt].l+seg[rt].r)/2){
        if(!seg[rt].ls){
            seg[rt].ls=++cnt;
            seg[cnt]=node(seg[rt].l,seg[rt].l+seg[rt].r>>1);
        }
        if(seg[seg[rt].ls].r - seg[seg[rt].ls].l + 1 > seg[seg[rt].ls].v){
            solve(L,R,seg[rt].ls);
            seg[rt].v=seg[seg[rt].ls].v + seg[seg[rt].rs].v;
            if(f)return;
        }
    }
    if(R>(seg[rt].l+seg[rt].r)/2){
        if(!seg[rt].rs){
            seg[rt].rs=++cnt;
            seg[cnt]=node((seg[rt].l+seg[rt].r)/2+1,seg[rt].r);
        }
        if(seg[seg[rt].rs].r - seg[seg[rt].rs].l + 1 > seg[seg[rt].rs].v)solve(L,R,seg[rt].rs);
    }
    seg[rt].v=seg[seg[rt].ls].v + seg[seg[rt].rs].v;
}
int main(){
    int t;cin>>t;
    while(t--){
        seg[1]=node(1,1e9);
        cnt=1;ans=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d%d",&p[i].first,&p[i].second);
        sort(p+1,p+1+n,cmp);
        for(int i=1;i<=n;i++){
            f=0;solve(p[i].first,p[i].second,1);
        }
        cout<<ans<<endl;
    }
    return 0;
}

势能线段树模板

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int maxn = 1e6+10;
int n,m;
ll sum[maxn<<2],maxv[maxn<<2],maxv2[maxn<<2],maxcnt[maxn<<2];
void push_up(int rt){
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
    maxv[rt]=max(maxv[rt<<1],maxv[rt<<1|1]);
    if(maxv[rt<<1] > maxv[rt<<1|1]){
        maxv2[rt]=max(maxv[rt<<1|1],maxv2[rt<<1]);
        maxcnt[rt]=maxcnt[rt<<1];
    }
    else if(maxv[rt<<1] == maxv[rt<<1|1]){
        maxv2[rt]=max(maxv2[rt<<1],maxv2[rt<<1|1]);
        maxcnt[rt]=maxcnt[rt<<1]+maxcnt[rt<<1|1];
    }
    else{
        maxv2[rt]=max(maxv[rt<<1],maxv2[rt<<1|1]);
        maxcnt[rt]=maxcnt[rt<<1|1];
    }
}
void build(int l,int r,int rt){
    if(l==r){
        scanf("%lld",sum+rt);
        maxv[rt]=sum[rt];maxv2[rt]=-1;maxcnt[rt]=1;return;
    }int mid=l+r>>1;
    build(lson);build(rson);push_up(rt);
}
void push_tag(int rt,int v){
    if(v>=maxv[rt])return;
    sum[rt]-=maxcnt[rt]*(maxv[rt]-v);maxv[rt]=v;
}
void push_down(int rt){
    push_tag(rt<<1,maxv[rt]);push_tag(rt<<1|1,maxv[rt]);
}
void update(int L,int R,int v,int l,int r,int rt){
    if(maxv[rt]<=v)return;
    int mid=l+r>>1;
    if(L<=l&&R>=r&&maxv2[rt]<v){
        push_tag(rt,v);
        return;
    }
    push_down(rt);
    if(L<=mid)update(L,R,v,lson);
    if(R>mid)update(L,R,v,rson);
    push_up(rt);
}
ll querysum(int L,int R,int l,int r,int rt){
    if(L<=l&&R>=r){
        return sum[rt];
    }ll ret=0,mid=l+r>>1;
    push_down(rt);
    if(L<=mid)ret+=querysum(L,R,lson);
    if(R>mid) ret+=querysum(L,R,rson);return ret;
}
ll querymax(int L,int R,int l,int r,int rt){
    if(L<=l&&R>=r){
        return maxv[rt];
    }ll ret=0,mid=l+r>>1;
    push_down(rt);
    if(L<=mid)ret=max(ret,querymax(L,R,lson));
    if(R>mid) ret=max(ret,querymax(L,R,rson));return ret;
}
int main(){
    int T;cin>>T;
    int tp,x,y,t;
    while(T--){
        scanf("%d%d",&n,&m);
        build(1,n,1);
        while(m--){
            scanf("%d%d%d",&tp,&x,&y);
            if(tp==0){
                scanf("%d",&t);update(x,y,t,1,n,1);
            }
            else if(tp==2)printf("%lld\n",querysum(x,y,1,n,1));
            else printf("%lld\n",querymax(x,y,1,n,1));
        }
    }
    return 0;
}

线段合并问题

//pku campus, unsolved

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e6+10;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define intmid int mid=l+r>>1
int lv[maxn<<2],rv[maxn<<2],mv[maxn<<2];
int n;
int a[maxn];
int last[maxn];
void push_up(int l,int r,int rt){
    intmid;
    if(lv[rt<<1]==mid-l+1)lv[rt]=lv[rt<<1]+lv[rt<<1|1];
    else lv[rt]=lv[rt<<1];
    if(rv[rt<<1|1]==r-mid)rv[rt]=rv[rt<<1|1]+rv[rt<<1];
    else rv[rt]=rv[rt<<1|1];
    mv[rt]=max(mv[rt<<1],mv[rt<<1|1]);
    mv[rt]=max(mv[rt],rv[rt<<1]+lv[rt<<1|1]);
}
void build(int l,int r,int rt){
    if(!lv[rt]&&!rv[rt]&&!mv[rt])return;
    if(l==r){
        lv[rt]=rv[rt]=mv[rt]=0;return;
    }intmid;
    build(lson);build(rson);push_up(l,r,rt);
}
void update(int p,int v,int l,int r,int rt){
    if(p<1||p>n)return;
    if(l==r){
        lv[rt]=rv[rt]=mv[rt]=v;return;
    }intmid;
    if(p<=mid)update(p,v,lson);
    else update(p,v,rson);
    push_up(l,r,rt);
}
int main(){
    int T;read(T);
    while(T--){
        read(n);
        for(int i=0;i<=n;i++)last[i]=0;
        build(1,n,1);
        int posl=1;
        int ans=0;
        for(int i=1;i<=n;i++){
            read(a[i]);
            if(ans==n)continue;
            if(last[a[i]]>=posl){
                while(last[a[i]]>=posl){
                    update(a[posl],0,1,n,1);posl++;
                }
            }
            update(a[i],1,1,n,1);
            last[a[i]]=i;
            ans=max(ans,mv[1]);
        }
        print(ans,'\n');
    }
    return 0;
}

扫描线求矩形面积并

#include <bits/stdc++.h>
using namespace std;
#define maxn 100005
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define intmid int mid=(l+r)>>1
typedef long long ll;
struct seg{
    int v;
    double l,r,h;
    seg(){}
    seg(int v,double l,double r,double h):v(v),l(l),r(r),h(h){}
    friend bool operator < (seg a,seg b){
        return a.h<b.h;
    }
}s[maxn];
int n;
int cnt[maxn<<2];
double x[maxn];
double one[maxn<<2],two[maxn<<2];
void push_up(int l,int r,int rt){
    if(cnt[rt]>=2)
        two[rt]=one[rt]=x[r+1]-x[l];//覆盖2次以上,直接算长度
    else if(cnt[rt]){
        one[rt]=x[r+1]-x[l];//覆盖1次以上,直接算长度
        if(l==r)two[rt]=0;
        else two[rt]=one[rt<<1]+one[rt<<1|1];//计算子区间的1次,加上这一次
    }
    else{
        one[rt]=one[rt<<1]+one[rt<<1|1];
        two[rt]=two[rt<<1]+two[rt<<1|1];
    }
}
void update(int L,int R,int v,int l,int r,int rt){
    if(L<=l&&r<=R){
        cnt[rt]+=v;push_up(l,r,rt);
        return;
    }
    intmid;
    if(L<=mid)update(L,R,v,lson);
    if(R>mid)update(L,R,v,rson);
    push_up(l,r,rt);
}
int main(){
    int t;scanf("%d",&t);
    while(t--){
        memset(cnt,0,sizeof(cnt));
        memset(one,0,sizeof(one));
        memset(two,0,sizeof(two));
        double x1,y1,x2,y2;
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            x[i]=x1;x[i+n]=x2;
            s[i]=seg(1,x1,x2,y1);
            s[i+n]=seg(-1,x1,x2,y2);
        }
        n<<=1;
        sort(x+1,x+1+n);
        sort(s+1,s+1+n);
        int len=unique(x+1,x+1+n)-(x+1);
        double ans=0;
        for(int i=1;i<n;i++)//数字代表点改为数字代表边,那么r值-1,即左闭右开
        {
            int l=lower_bound(x+1,x+1+len,s[i].l)-x;
            int r=lower_bound(x+1,x+1+len,s[i].r)-x;
            update(l,r-1,s[i].v,1,len,1);
            ans+=two[1]*(s[i+1].h-s[i].h);
        }
        printf("%.2f\n",ans);
    }
    return 0;
}

扫描线求矩形周长并

using namespace std;
typedef long long LL;
const int N = 500010;
const int INF = 1e8;
struct line//线段树节点
{
    int l, r;//左右端点
    int lp, rp;//判断左右端点是否存在,存在为1,不存在未0
    int cnt, len;//cnt代表是否被覆盖,0代表未被完全覆盖,1代表被完全覆盖
    int num;//记录区间内的线段数目
}tree[4*N];
struct node//保存线段
{
    int l, r, h;
    int f;
    bool operator < (const struct node & tmp) const{
        return h < tmp.h;
    }
}seg[4*N];
int x[N];
void build(int i, int l, int r){
    tree[i].l = l;tree[i].r = r;
    tree[i].len = tree[i].cnt = tree[i].num = 0;
    tree[i].lp = tree[i].rp = 0;
    if(l == r) return;
    int mid = (l+r) >> 1;
    build(i*2, l, mid);build(i*2+1, mid+1, r);
}
int binsearch(int key, int k){
    int high = k;
    int low = 1;
    while(high >= low) {
        int mid = (high+low) >> 1;
        if(x[mid] == key)return mid;
        else if(x[mid] < key) low = mid+1;
        else high = mid-1;
    }
    return -1;
}
void maintain(int i){
    if(tree[i].cnt){
        tree[i].len = x[tree[i].r+1]-x[tree[i].l];
        tree[i].lp = tree[i].rp = tree[i].num = 1;
        return;
    }
    if(tree[i].l == tree[i].r){
        tree[i].len = tree[i].lp = tree[i].rp = tree[i].num = 0;
        return;
    }
    tree[i].len = tree[i*2].len+tree[i*2+1].len;
    tree[i].lp = tree[i*2].lp;
    tree[i].rp = tree[i*2+1].rp;
    tree[i].num = (tree[i*2].num+tree[i*2+1].num-(tree[i*2].rp&&tree[i*2+1].lp));
}
void update(int i, int l, int r, int f){
    if(tree[i].l == l && tree[i].r == r){
        tree[i].cnt += f;
        maintain(i);
        return;
    }
    int mid = (tree[i].l+tree[i].r) >> 1;
    if(mid >= r)
        update(i*2, l, r, f);
    else if(mid < l)
        update(i*2+1, l, r, f);
    else{
        update(i*2, l, mid, f);
        update(i*2+1, mid+1, r, f);
    }
    maintain(i);
}
int main(){
   // freopen("in.txt", "r", stdin);
    int n, x1, y1, x2, y2;
    while(~scanf("%d", &n)){
        int num = 1;
        for(int i = 1; i <= n; i++){
            scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
            seg[num] = (struct node){x1, x2, y1, 1};
            x[num++] = x1;
            seg[num] = (struct node){x1, x2, y2, -1};
            x[num++] = x2;
        }
        sort(seg+1, seg+num);
        sort(x+1, x+num);
        int k = 1;
        for(int i = 2; i < num; i++){
            if(x[i-1] != x[i]) x[++k] = x[i];
        }
        build(1, 1, k);
        int ans = 0;
        int pre = 0;
        for(int i = 1; i < num; i++){
            int l = binsearch(seg[i].l, k);
            int r = binsearch(seg[i].r, k)-1;
            update(1, l, r, seg[i].f);
            int t = abs(tree[1].len-pre);
            pre = tree[1].len;
            ans += t;
            if(i < num-1) ans += (seg[i+1].h-seg[i].h)*2*tree[1].num;
        }
        printf("%d\n", ans);
    }
    return 0;
}

LCA

rmq版本的LCA

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn = 5e5+10;
int rmq[maxn<<1];
struct ST{
    int mm[maxn<<1];
    int dp[maxn<<1][20];
    void init(int n){
        mm[0]=-1;
        for(int i=1;i<=n;i++){
            mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];dp[i][0]=i;
        }
        for(int j=1;j<=mm[n];j++){
            for(int i=1;i+(1<<j)-1<=n;i++){
                dp[i][j]=rmq[dp[i][j-1]]<rmq[dp[i+(1<<(j-1))][j-1]]?
                dp[i][j-1]:dp[i+(1<<(j-1))][j-1];
            }
        }
    }
    int query(int a,int b){
        if(a>b)swap(a,b);
        int k=mm[b-a+1];
        return rmq[dp[a][k]]<=rmq[dp[b-(1<<k)+1][k]]?
        dp[a][k]:dp[b-(1<<k)+1][k];
    }
}st;
int F[maxn<<1],p[maxn];
int cnt;
struct edge{int to,nxt;};
edge e[maxn<<1];
int tot,head[maxn];
void adde(int u,int v){
    e[tot].to=v;
    e[tot].nxt=head[u];
    head[u]=tot++;
}
void dfs(int u,int pre,int dep){
    F[++cnt]=u;
    rmq[cnt]=dep;
    p[u]=cnt;
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(v==pre)continue;
        dfs(v,u,dep+1);
        F[++cnt]=u;
        rmq[cnt]=dep;
    }
}
void lca_init(int rt,int num){
    dfs(rt,rt,0);
    st.init(2*num-1);
}
int query_lca(int u,int v){return F[st.query(p[u],p[v])];}

int main(){
    memset(head,-1,sizeof head);
    int n,m,s;scanf("%d%d%d",&n,&m,&s);
    int u,v;
    for(int i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        adde(u,v);adde(v,u);
    }
    lca_init(s,n);
    while(m--){
        scanf("%d%d",&u,&v);
        printf("%d\n",query_lca(u,v));
    }
    return 0;
}

倍增法求LCA

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn = 1e4+10;
int n,q;

/*********************LCA*********************/
int dis[maxn];
int cnt;
struct edge{int to,nxt,v;};
edge e[maxn<<1];
int tot,head[maxn];
void adde(int u,int v,int w){
    e[tot].to=v;
    e[tot].v=w;
    e[tot].nxt=head[u];
    head[u]=tot++;
}
int fa[maxn][16];
int dep[maxn];
void bfs(int rt){
    queue<int> q;
    dep[rt]=0;
    fa[rt][0]=rt;
    q.push(rt);
    while(!q.empty()){
        int tmp=q.front();q.pop();
        for(int i=1;i<16;i++)
            fa[tmp][i]=fa[fa[tmp][i-1]][i-1];
        for(int i=head[tmp];i!=-1;i=e[i].nxt){
            int v=e[i].to;
            if(v==fa[tmp][0])continue;
            dep[v]=dep[tmp]+1;
            dis[v]=dis[tmp]+e[i].v;
            fa[v][0]=tmp;
            q.push(v);
        }
    }
}
int query_lca(int u,int v){
    if(dep[u]>dep[v])swap(u,v);
    int hu=dep[u],hv=dep[v];
    int tu=u,tv=v;
    for(int det=hv-hu,i=0;det;det>>=1,i++)
        if(det&1)tv=fa[tv][i];
    if(tu==tv)return tu;
    for(int i=15;i>=0;i--){
        if(fa[tu][i]==fa[tv][i])continue;
        tu=fa[tu][i];tv=fa[tv][i];
    }return fa[tu][0];
}
int query_kth(int u,int k){
    for(int det=k,i=0;det;det>>=1,i++)if(det&1)u=fa[u][i];return u;
}
/*********************LCA*********************/

int main(){
    int u,v,w;
    char op[10];
    int t;cin>>t;
    while(t--){
        memset(head,-1,sizeof head);tot=0;
        scanf("%d",&n);
        for(int i=1;i<n;i++){
            scanf("%d%d%d",&u,&v,&w);
            adde(u,v,w);adde(v,u,w);
        }bfs(1);
        while(scanf("%s",op)!=EOF&&op[1]!='O'){
            if(op[1]=='I'){
                scanf("%d%d",&u,&v);
                printf("%d\n",dis[u]+dis[v]-2*dis[query_lca(u,v)]);
            }
            else{
                scanf("%d%d%d",&u,&v,&w);
                int fa=query_lca(u,v);
                if(dep[u]-dep[fa]+1>=w){
                    printf("%d\n",query_kth(u,w-1));
                }
                else{
                    printf("%d\n",query_kth(v,dep[v]-dep[fa]+1-(w-(dep[u]-dep[fa]))));
                }
            }
        }puts("");
    }
    return 0;
}

树链剖分求LCA

struct edge{int nxt,to,v;}e[maxn<<1];
int cnt,head[maxn];
int fa[maxn],deep[maxn],siz[maxn],son[maxn],rk[maxn],top[maxn],id[maxn];
int dis[maxn];
void adde(int u,int v,int w){
    e[++cnt].nxt=head[u];
    e[cnt].to=v;
    e[cnt].v=w;
    head[u]=cnt;
}
void dfs1(int u,int pre,int dep){
    fa[u]=pre;deep[u]=dep;siz[u]=1;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v==pre)continue;
        dis[v]=dis[u]+e[i].v;
        dfs1(v,u,dep+1);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]])son[u]=v;
    }
}
void dfs2(int u,int t){
    top[u]=t;id[u]=++cnt;rk[cnt]=u;
    if(!son[u])return;
    dfs2(son[u],t);
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v!=son[u]&&v!=fa[u])dfs2(v,v);
    }
}
int query_lca(int x,int y){
    while(top[x]!=top[y]){
        if(deep[top[x]]<deep[top[y]])swap(x,y);
        x=fa[top[x]];
    }return deep[x]<deep[y]?x:y;
}

树链剖分

点权模板

luogu qtree3。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int maxn = 1e5+10;
int n,q;
struct edge{int nxt,to;}e[maxn<<1];
int cnt,head[maxn];
int fa[maxn],deep[maxn],siz[maxn],son[maxn],rk[maxn],top[maxn],id[maxn];
void adde(int u,int v){
    e[++cnt].nxt=head[u];
    e[cnt].to=v;
    head[u]=cnt;
}
void dfs1(int u,int pre,int dep){
    fa[u]=pre;deep[u]=dep;siz[u]=1;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v==pre)continue;
        dfs1(v,u,dep+1);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]])son[u]=v;
    }
}
void dfs2(int u,int t){
    top[u]=t;id[u]=++cnt;rk[cnt]=u;
    if(!son[u])return;
    dfs2(son[u],t);
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v!=son[u]&&v!=fa[u])dfs2(v,v);
    }
}
/***************************/
int sum[maxn<<2];
void push_up(int rt){
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void update(int L,int R,int l,int r,int rt){
    if(L<=l&&R>=r){
        sum[rt]^=1;
        return;
    }
    int mid=l+r>>1;
    if(L<=mid)update(L,R,lson);
    if(R>mid) update(L,R,rson);
    push_up(rt);
}
int query(int L,int R,int l,int r,int rt){
    if(sum[rt]==0)return 0;
    if(l==r)return l;
    int mid=l+r>>1;
    if(L<=l&&R>=r){
        if(sum[rt<<1])return query(L,R,lson);
        return query(L,R,rson);
    }
    int ret=0;
    if(L<=mid)ret=query(L,R,lson);
    if(ret)return ret;
    if(R>mid)ret=query(L,R,rson);
    return ret;
}
/***************************/
int qquery(int x,int y){
    int ans=0;
    while(top[x]!=top[y]){
        if(deep[top[x]] < deep[top[y]])swap(x,y);
        int tmp=query(id[top[x]],id[x],1,n,1);
        if(tmp)ans=tmp;
        x=fa[top[x]];
    }
    if(deep[x]>deep[y])swap(x,y);
    int tmp=query(id[x],id[y],1,n,1);
    if(tmp)ans=tmp;
    return ans;
}
void uupdate(int x,int y){
    while(top[x]!=top[y]){
        if(deep[top[x]] < deep[top[y]])swap(x,y);
        update(id[top[x]],id[x],1,n,1);
        x=fa[top[x]];
    }
    if(deep[x]>deep[y])swap(x,y);
    update(id[x],id[y],1,n,1);
}
/***************************/
int u,v,w;
char op[10];

int main(){
    scanf("%d%d",&n,&q);
    for(int i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        adde(u,v);adde(v,u);
    }
    rk[0]=-1;
    cnt=0;dfs1(1,0,1);dfs2(1,1);
    while(q--){
        scanf("%s",op);
        if(op[0]=='0'){
            scanf("%d",&u);
            uupdate(u,u);
        }else{
            scanf("%d",&u);
            printf("%d\n",rk[qquery(1,u)]);
        }
    }
    return 0;
}

基于边权

struct edge{int nxt,to,v;}e[maxn<<1];
int cnt,head[maxn];
int fa[maxn],deep[maxn],siz[maxn],son[maxn],rk[maxn],top[maxn],id[maxn];
int dis[maxn];
void adde(int u,int v,int w){
    e[++cnt].nxt=head[u];
    e[cnt].to=v;
    e[cnt].v=w;
    head[u]=cnt;
}
void dfs1(int u,int pre,int dep){
    fa[u]=pre;deep[u]=dep;siz[u]=1;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v==pre)continue;
        dis[v]=dis[u]+e[i].v;
        dfs1(v,u,dep+1);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]])son[u]=v;
    }
}
void dfs2(int u,int t){
    top[u]=t;id[u]=++cnt;rk[cnt]=u;
    if(!son[u])return;
    dfs2(son[u],t);
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v!=son[u]&&v!=fa[u])dfs2(v,v);
    }
}
int qquery(int x,int y){
    int ans=0;
    while(top[x]!=top[y]){
        if(deep[top[x]] < deep[top[y]])swap(x,y);
        ans=max(ans , query(id[top[x]],id[x],1,n,1));
        x=fa[top[x]];
    }
    if(x==y)return ans;
    if(deep[x]>deep[y])swap(x,y);
    ans = max(ans , query(id[son[x]],id[y],1,n,1));
    return ans;
}
void uupdate(int x,int y,int v){
    while(top[x]!=top[y]){
        if(deep[top[x]] < deep[top[y]])swap(x,y);
        update(id[top[x]],id[x],v,1,n,1);
        x=fa[top[x]];
    }
    if(x==y)return;
    if(deep[x]>deep[y])swap(x,y);
    update(id[son[x]],id[y],v,1,n,1);
}

虚树 东北四省赛D

#include<cstdio>
#include<cstring>
#include<cstdlib>
const int N=500010,M=2010,K=M*4,inf=~0U>>1;
int Case,n,m,i,o,x,y,z,root,op[M][4];
int vip[N],g[N],v[N<<1],nxt[N<<1],ed,f[N],d[N],id[N],cnt;
int at[K],G[K],W[K],NXT[K],F[K],D[K];
int vv[K],ve[K];
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
inline void addedge(int x,int y,int z){NXT[y]=G[x];G[x]=y;W[y]=z;}
inline void umin(int&a,int b){a>b?(a=b):0;}
inline void umax(int&a,int b){a<b?(a=b):0;}
inline int abs(int x){return x>0?x:-x;}
inline void swap(int&a,int&b){int c=a;a=b;b=c;}
void dfs(int x){
  int deg=0;
  for(int i=g[x];i;i=nxt[i]){
    int u=v[i];
    if(u==f[x])continue;
    f[u]=x;
    d[u]=d[x]+1;
    dfs(u);
    if(!id[u])continue;
    deg++;
    id[x]^=id[u];
  }
  if(deg>1)vip[x]=1;
  if(!vip[x])return;
  id[x]=++cnt;
  at[cnt]=x;
  for(int i=g[x];i;i=nxt[i]){
    int u=v[i];
    if(u==f[x])continue;
    u=id[u];
    if(!u)continue;
    addedge(cnt,u,d[at[u]]-d[x]-1);
  }
}
void dfs2(int x,int y){
  F[x]=y;
  D[x]=D[y]+1;
  for(int i=G[x];i;i=NXT[i])dfs2(i,x);
}
int main(){
  int size=64<<20;//64MB
  char *p=(char*)std::malloc(size)+size;
  __asm__("movl %0, %%esp\n"::"r"(p));
  scanf("%d",&Case);
  while(Case--){
    scanf("%d%d",&n,&m);
    for(ed=cnt=i=0;i<=n;i++)f[i]=d[i]=id[i]=vip[i]=g[i]=0;
    memset(G,0,sizeof G);
    memset(W,0,sizeof W);
    memset(F,0,sizeof F);
    memset(D,0,sizeof D);
    memset(vv,0,sizeof vv);
    memset(ve,0,sizeof ve);
    for(i=1;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x);
    for(i=1;i<=m;i++){
      scanf("%d%d%d",&o,&x,&y);
      vip[x]=vip[y]=1;
      op[i][0]=o;
      op[i][1]=x;
      op[i][2]=y;
      if(o==1||o==2||o==3||o==7)scanf("%d",&op[i][3]);
    }
    for(i=1;i<=n;i++)if(vip[i])root=i;
    dfs(root);
    dfs2(id[root],0);
    for(i=1;i<=m;i++){
      o=op[i][0];
      x=id[op[i][1]];
      y=id[op[i][2]];
      z=op[i][3];
      if(o==1){
        while(x!=y){
          if(D[x]<D[y])swap(x,y);
          vv[x]+=z;
          ve[x]+=z;
          x=F[x];
        }
        vv[x]+=z;
      }
      if(o==2){
        while(x!=y){
          if(D[x]<D[y])swap(x,y);
          vv[x]^=z;
          ve[x]^=z;
          x=F[x];
        }
        vv[x]^=z;
      }
      if(o==3){
        while(x!=y){
          if(D[x]<D[y])swap(x,y);
          if(vv[x]>=z)vv[x]-=z;
          if(ve[x]>=z)ve[x]-=z;
          x=F[x];
        }
        if(vv[x]>=z)vv[x]-=z;
      }
      if(o==4){
        long long ans=0;
        while(x!=y){
          if(D[x]<D[y])swap(x,y);
          ans+=vv[x];
          ans+=1LL*ve[x]*W[x];
          x=F[x];
        }
        printf("%lld\n",ans+vv[x]);
      }
      if(o==5){
        int ans=0;
        while(x!=y){
          if(D[x]<D[y])swap(x,y);
          ans^=vv[x];
          if(W[x]&1)ans^=ve[x];
          x=F[x];
        }
        printf("%d\n",ans^vv[x]);
      }
      if(o==6){
        int mi=inf,ma=0;
        while(x!=y){
          if(D[x]<D[y])swap(x,y);
          umin(mi,vv[x]);
          umax(ma,vv[x]);
          if(W[x]){
            umin(mi,ve[x]);
            umax(ma,ve[x]);
          }
          x=F[x];
        }
        umin(mi,vv[x]);
        umax(ma,vv[x]);
        printf("%d\n",ma-mi);
      }
      if(o==7){
        int ans=inf;
        while(x!=y){
          if(D[x]<D[y])swap(x,y);
          umin(ans,abs(vv[x]-z));
          if(W[x])umin(ans,abs(ve[x]-z));
          x=F[x];
        }
        umin(ans,abs(vv[x]-z));
        printf("%d\n",ans);
      }
    }
  }
}

主席树

省内存

void update(int &u,int p,int l,int r){
    ++tot;lson[tot]=lson[u];rson[tot]=rson[u];c[tot]=c[u]+1;
    u=tot;
    if(l==r)return;
    int mid=l+r>>1;
    if(p<=mid)update(lson[u],p,l,mid);
    else      update(rson[u],p,mid+1,r);
}

可持久化

const int maxn = 2e6+10;
int n,q,m,tot;
int a[maxn],t[maxn];
int T[maxn],lson[maxn*22],rson[maxn*22],c[maxn*22];
void init_hs(){
    for(int i=1;i<=n;i++){
        t[i]=a[i];
    }sort(t+1,t+1+n);
    m = unique(t+1,t+1+n)-t-1;
}
int build(int l,int r){
    int root=tot++;
    c[root]=0;
    if(l!=r){
        int mid=l+r>>1;
        lson[root]=build(l,mid);
        rson[root]=build(mid+1,r);
    }
    return root;
}
int hs(int x){return lower_bound(t+1,t+1+m,x)-t;}
int update(int root,int pos,int val){
    int newroot=tot++,tmp=newroot;
    c[newroot]=val;
    int l=1,r=n;
    while(l<r){
        int mid=l+r>>1;
        if(pos<=mid){
            lson[newroot]=tot++;
            rson[newroot]=rson[root];
            newroot=lson[newroot];
            root=lson[root];
            r=mid;
        }
        else{
            rson[newroot]=tot++;
            lson[newroot]=lson[root];
            newroot=rson[newroot];
            root=rson[root];
            l=mid+1;
        }
        c[newroot]=val;
    }
    return tmp;
}
int query(int root,int id){
    int l=1,r=n;
    while(l<r){
        int mid=l+r>>1;
        if(id<=mid){
            r=mid;
            root=lson[root];
        }
        else{
            l=mid+1;
            root=rson[root];
        }
    }
    return c[root];
}
int v[maxn];
int main(){
    read(n,m);
    for(int i=1;i<=n;i++){
        read(a[i]);
    }
    T[n+1]=build(1,n);
    for(int i=n;i;i--){
        T[i]=update(T[i+1],i,a[i]);
    }
    v[0]=T[1];
    int op,vi,loci,valuei;
    for(int i=1;i<=m;i++){
        read(vi,op);
        if(op==1){
            read(loci,valuei);
            v[i]=update(v[vi],loci,valuei);
        }
        else{
            read(loci);
            v[i]=v[vi];
            print(query(v[i],loci),"\n");
        }
    }
    return 0;
}

静态区间第k大

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
int n,q,m,tot;
int a[maxn],t[maxn];
int T[maxn*20],lson[maxn*20],rson[maxn*20],c[maxn*20];
void init_hs(){
    for(int i=1;i<=n;i++){
        t[i]=a[i];
    }
    sort(t+1,t+1+n);
    m = unique(t+1,t+1+n)-t-1;
}
int build(int l,int r){
    int root=tot++;
    c[root]=0;
    if(l!=r){
        int mid=l+r>>1;
        lson[root]=build(l,mid);
        rson[root]=build(mid+1,r);
    }
    return root;
}
int hs(int x){
    return lower_bound(t+1,t+1+m,x)-t;
}
int update(int root,int pos,int val){
    int newroot=tot++,tmp=newroot;
    c[newroot]=c[root]+val;
    int l=1,r=m;
    while(l<r){
        int mid=l+r>>1;
        if(pos<=mid){
            lson[newroot]=tot++;
            rson[newroot]=rson[root];
            newroot=lson[newroot];
            root=lson[root];
            r=mid;
        }
        else{
            rson[newroot]=tot++;
            lson[newroot]=lson[root];
            newroot=rson[newroot];
            root=rson[root];
            l=mid+1;
        }
        c[newroot]=c[root]+val;
    }
    return tmp;
}
int query(int left_root,int right_root,int k){
    int l=1,r=m;
    while(l<r){
        int mid=l+r>>1;
        if(c[lson[left_root]] - c[lson[right_root]] >= k){
            r=mid;
            left_root=lson[left_root];
            right_root=lson[right_root];
        }
        else{
            l=mid+1;
            k-=c[lson[left_root]]-c[lson[right_root]];
            left_root=rson[left_root];
            right_root=rson[right_root];
        }
    }
    return l;
}
int main(){
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++){
        scanf("%d",a+i);
    }
    init_hs();
    T[n+1]=build(1,m);
    for(int i=n;i;i--){
        int pos = hs(a[i]);
        T[i] = update(T[i+1],pos,1);
    }
    while(q--){
        int l,r,k;
        scanf("%d%d%d",&l,&r,&k);
        printf("%d\n",t[query(T[l],T[r+1],k)]);
    }
    return 0;
}

动态区间第k大,主席树+树状数组

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
int n,m;
struct node{int op,l,r,k;}q[maxn];
int a[maxn];
char op[2];
int lson[maxn<<7],rson[maxn<<7],c[maxn<<7];
int T[maxn],S[maxn],use[maxn],tot;
int cntn;
int dt[maxn];
int hs(int x){return lower_bound(dt+1,dt+1+cntn,x)-dt;}
int build(int l,int r){
    int rt=tot++;c[rt]=0;
    if(l!=r){
        int mid=l+r>>1;
        lson[rt]=build(l,mid);rson[rt]=build(mid+1,r);
    }return rt;
}
int ins(int rt,int pos,int val){
    int nrt=tot++,tmp=nrt;
    int l=1,r=cntn;
    c[nrt]=c[rt]+val;
    while(l<r){
        int mid=l+r>>1;
        if(pos<=mid){
            lson[nrt]=tot++;
            rson[nrt]=rson[rt];
            nrt=lson[nrt];
            rt=lson[rt];
            r=mid;
        }
        else{
            lson[nrt]=lson[rt];
            rson[nrt]=tot++;
            nrt=rson[nrt];
            rt=rson[rt];
            l=mid+1;
        }
        c[nrt]=c[rt]+val;
    }return tmp;
}
inline int lowbit(int x){return x&(-x);}
int sum(int x){
    int ans=0;
    while(x){
        ans+=c[lson[use[x]]];x-=lowbit(x);
    }return ans;
}
void update(int x,int pos,int val){
    while(x<=n){
        S[x]=ins(S[x],pos,val);x+=lowbit(x);
    }
}
int query(int L,int R,int k){
    int lrt=T[L-1];
    int rrt=T[R];
    int l=1,r=cntn;
    for(int i=L-1;i;i-=lowbit(i))use[i]=S[i];
    for(int i=R;i;i-=lowbit(i))use[i]=S[i];
    while(l<r){
        int mid=l+r>>1;
        int tmp=sum(R)-sum(L-1)+c[lson[rrt]]-c[lson[lrt]];
        if(tmp>=k){
            r=mid;
            for(int i=L-1;i;i-=lowbit(i))use[i]=lson[use[i]];
            for(int i=R;i;i-=lowbit(i))use[i]=lson[use[i]];
            lrt=lson[lrt];rrt=lson[rrt];
        }
        else{
            l=mid+1;k-=tmp;
            for(int i=L-1;i;i-=lowbit(i))use[i]=rson[use[i]];
            for(int i=R;i;i-=lowbit(i))use[i]=rson[use[i]];
            lrt=rson[lrt];rrt=rson[rrt];
        }
    }return l;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",a+i),dt[++cntn]=a[i];
    for(int i=1;i<=m;i++){
        scanf("%s",op);
        if(op[0]=='Q')q[i].op=0,scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].k);
        else scanf("%d%d",&q[i].l,&q[i].k),dt[++cntn]=q[i].k,q[i].op=1;
    }
    sort(dt+1,dt+1+cntn);cntn=unique(dt+1,dt+1+cntn)-dt-1;
    T[0]=build(1,cntn);
    for(int i=1;i<=n;i++)T[i]=ins(T[i-1],hs(a[i]),1),S[i]=T[0];
    for(int i=1;i<=m;i++){
        if(q[i].op){
            update(q[i].l,hs(a[q[i].l]),-1);
            update(q[i].l,hs(q[i].k),1);
            a[q[i].l]=q[i].k;
        }
        else printf("%d\n",dt[query(q[i].l,q[i].r,q[i].k)]);
    }
    return 0;
}

树上区间第k大

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
int n,q,cntn,TOT;
int a[maxn],dt[maxn];
int T[maxn],lson[maxn<<5],rson[maxn<<5],c[maxn<<5];
void init_hash(){
    sort(dt+1,dt+1+cntn);
    cntn=unique(dt+1,dt+1+cntn)-dt-1;
}
int hs(int x){return lower_bound(dt+1,dt+1+cntn,x)-dt;}
int build(int l,int r){
    int rt=TOT++;
    c[rt]=0;
    if(l!=r){
        int mid=l+r>>1;
        lson[rt]=build(l,mid);rson[rt]=build(mid+1,r);
    }return rt;
}
int update(int rt,int pos,int val){
    int nrt=TOT++,tmp=nrt;
    c[nrt]=c[rt]+val;
    int l=1,r=cntn;
    while(l<r){
        int mid=l+r>>1;
        if(pos<=mid){
            lson[nrt]=TOT++,rson[nrt]=rson[rt];nrt=lson[nrt];rt=lson[rt];r=mid;
        }else{
            rson[nrt]=TOT++,lson[nrt]=lson[rt];nrt=rson[nrt];rt=rson[rt];l=mid+1;
        }c[nrt]=c[rt]+val;
    }return tmp;
}
int query(int lrt,int rrt,int LCA,int k){
    int lcart=T[LCA];
    int pos=hs(a[LCA]);
    int l=1,r=cntn;
    while(l<r){
        int mid=l+r>>1;
        int tmp = c[lson[lrt]]+c[lson[rrt]]-2*c[lson[lcart]]+(pos>=l&&pos<=mid);
        if(tmp>=k){
            lrt=lson[lrt];rrt=lson[rrt];lcart=lson[lcart];r=mid;
        }else{
            k-=tmp;l=mid+1;
            lrt=rson[lrt];rrt=rson[rrt];lcart=rson[lcart];
        }
    }return l;
}
int rmq[maxn<<1];
struct ST{
    int mm[maxn<<1];
    int dp[maxn<<1][20];
    void init(int n){
        mm[0]=-1;
        for(int i=1;i<=n;i++){
            mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];dp[i][0]=i;
        }
        for(int j=1;j<=mm[n];j++){
            for(int i=1;i+(1<<j)-1<=n;i++){
                dp[i][j]=rmq[dp[i][j-1]]<rmq[dp[i+(1<<(j-1))][j-1]]?
                dp[i][j-1]:dp[i+(1<<(j-1))][j-1];
            }
        }
    }
    int query(int a,int b){
        if(a>b)swap(a,b);
        int k=mm[b-a+1];
        return rmq[dp[a][k]]<=rmq[dp[b-(1<<k)+1][k]]?
        dp[a][k]:dp[b-(1<<k)+1][k];
    }
}st;
int F[maxn<<1],p[maxn];
int cnt;
struct edge{int to,nxt;};
edge e[maxn<<1];
int tot,head[maxn];
void adde(int u,int v){
    e[tot].to=v;
    e[tot].nxt=head[u];
    head[u]=tot++;
}
void dfs(int u,int pre,int dep){
    F[++cnt]=u;
    rmq[cnt]=dep;
    p[u]=cnt;
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(v==pre)continue;
        dfs(v,u,dep+1);
        F[++cnt]=u;
        rmq[cnt]=dep;
    }
}
void lca_init(int rt,int num){
    dfs(rt,rt,0);
    st.init(2*num-1);
}
int query_lca(int u,int v){return F[st.query(p[u],p[v])];}
void dfs_build(int u,int pre){
    int pos=hs(a[u]);
    T[u]=update(T[pre],pos,1);
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(v==pre)continue;
        dfs_build(v,u);
    }
}
int ls=0;
int main(){
    memset(head,-1,sizeof head);
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)scanf("%d",a+i),dt[++cntn]=a[i];
    init_hash();
    int u,v,k;
    for(int i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        adde(u,v);adde(v,u);
    }
    lca_init(1,n);
    T[0]=build(1,cntn);
    dfs_build(1,0);
    while(q--){
        scanf("%d%d%d",&u,&v,&k);u^=ls;
        printf("%d\n",ls=dt[query(T[u],T[v],query_lca(u,v),k)]);
    }
    return 0;
}

区间修改历史版本线段树

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

#define dlson l,mid,lson[nrt]
#define drson mid+1,r,rson[nrt]

const int maxn = 2e5+10;
int n,m;
int T[maxn],tot,lson[maxn<<5],rson[maxn<<5];
ll c[maxn<<5],cur[maxn<<5];
void push_up(int rt){
    c[rt]=c[lson[rt]]+c[rson[rt]];
}
void build(int l,int r,int &nrt){
    nrt=tot++;cur[nrt]=0;
    if(l==r){
        scanf("%lld",c+nrt);return;
    }int mid=l+r>>1;
    build(dlson);build(drson);push_up(nrt);
}
void update(int L,int R,ll val,int l,int r,int &nrt,int rt){
    nrt=tot++;
    lson[nrt]=lson[rt];
    rson[nrt]=rson[rt];
    c[nrt]=c[rt]+(R-L+1)*val;
    cur[nrt]=cur[rt];
    if(L==l&&R==r){
        cur[nrt]+=val;return;
    }
    int mid=l+r>>1;
    if(R<=mid)update(L,R,val,dlson,lson[rt]);
    else if(L>mid)update(L,R,val,drson,rson[rt]);
    else update(L,mid,val,dlson,lson[rt]),update(mid+1,R,val,drson,rson[rt]);
}
ll query(int L,int R,int l,int r,int nrt,ll add){
    if(L==l&&R==r)return c[nrt]+(R-L+1)*add;
    int mid=l+r>>1;
    if(R<=mid)return query(L,R,dlson,add+cur[nrt]);
    else if(L>mid)return query(L,R,drson,add+cur[nrt]);
    return query(L,mid,dlson,add+cur[nrt])+query(mid+1,R,drson,add+cur[nrt]);
}
int main(){
    while(scanf("%d%d",&n,&m)!=EOF){
    tot=0;
    build(1,n,T[0]);
    int time=0;
    char op[2];
    int l,r,d,t;
    for(int i=1;i<=m;i++){
        scanf("%s",op);
        if(op[0]=='C'){
            scanf("%d%d%d",&l,&r,&d);
            update(l,r,d,1,n,T[time+1],T[time]);time++;
        }
        else if(op[0]=='Q'){
            scanf("%d%d",&l,&r);
            printf("%lld\n",query(l,r,1,n,T[time],0));
        }
        else if(op[0]=='H'){
            scanf("%d%d%d",&l,&r,&t);
            printf("%lld\n",query(l,r,1,n,T[t],0));
        }
        else{
            scanf("%d",&t);
            tot=T[t+1];
            time=t;
        }
    }
    }
    return 0;
}

平衡树

treap

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
const int inf = 0x3f3f3f3f;
int tot=0,r=0;
int siz[maxn],v[maxn],num[maxn],rd[maxn],son[maxn][2];
/**subtree size,point value,point time,rand value,son**/
void push_up(int rt){
    siz[rt]=siz[son[rt][0]]+siz[son[rt][1]]+num[rt];
}
void rotate(int &rt,int d){
    int k=son[rt][d^1];
    son[rt][d^1]=son[k][d];
    son[k][d]=rt;
    push_up(rt);push_up(k);
    rt=k;
}
void ins(int &rt,int x){
    if(!rt){
        rt=++tot;
        siz[rt]=num[rt]=1;
        v[rt]=x;rd[rt]=rand();
        return;
    }
    if(v[rt]==x){
        num[rt]++;siz[rt]++;return;
    }
    int d=(x>v[rt]);
    ins(son[rt][d],x);
    if(rd[rt]<rd[son[rt][d]])rotate(rt,d^1);
    push_up(rt);
}
void del(int &rt,int x){
    if(!rt)return;
    if(x<v[rt])del(son[rt][0],x);
    else if(x>v[rt])del(son[rt][1],x);
    else{
        if(!son[rt][0]&&!son[rt][1]){
            num[rt]--;siz[rt]--;
            if(!num[rt])rt=0;
        }
        else if(son[rt][0]&&!son[rt][1]){
            rotate(rt,1);
            del(son[rt][1],x);
        }
        else if(!son[rt][0]&&son[rt][1]){
            rotate(rt,0);
            del(son[rt][0],x);
        }
        else{
            int d=(rd[son[rt][0]] >rd[son[rt][1]] );
            rotate(rt,d);
            del(son[rt][d],x);
        }
    }push_up(rt);
}
int rk(int rt,int x){
    if(!rt)return 0;
    if(v[rt]==x)return siz[son[rt][0]]+1;
    if(v[rt]<x)return siz[son[rt][0]]+num[rt]+rk(son[rt][1],x);
    return rk(son[rt][0],x);
}
int find(int rt,int x){
    if(!rt)return 0;
    if(siz[son[rt][0]]>=x)return find(son[rt][0],x);
    if(siz[son[rt][0]]+num[rt]<x)return find(son[rt][1],x-num[rt]-siz[son[rt][0]]);
    return v[rt];
}
int pre(int rt,int x){
    if(!rt)return -inf;
    if(v[rt]>=x)return pre(son[rt][0],x);
    return max(v[rt],pre(son[rt][1],x));
}
int nxt(int rt,int x){
    if(!rt)return inf;
    if(v[rt]<=x)return nxt(son[rt][1],x);
    return min(v[rt],nxt(son[rt][0],x));
}
int main(){
    int n;scanf("%d",&n);
    int op,x;
    for(int i=0;i<n;i++){
        scanf("%d%d",&op,&x);
        if(op==1)ins(r,x);
        else if(op==2)del(r,x);
        else if(op==3)printf("%d\n",rk(r,x));
        else if(op==4)printf("%d\n",find(r,x));
        else if(op==5)printf("%d\n",pre(r,x));
        else printf("%d\n",nxt(r,x));
    }
    return 0;
}

splay copy from kuangbin

/*
 * 给定一个数列a1,a2,...an
 * 进行以下6种操作
 * ADD x y D :给第x个数到第y个数加D(增加一个add进行延迟标记)
 * REVERSE x y :反转[x,y]之间的数(伸展树经典操作)
 * REVOLVE x y T:循环右移T次(先把T对长度进行取模,然后就相当于把[y-T+1,y]放在[x,y-T]前面)
 * INSERT x P:在第x个数后面插入P (经典的插入)
 * DELETE x:删除第x个数(删除操作)
 * MIN x y:查询[x,y]之间最小的数(标记)
 * CUT x y z 把区间[l,r]切断,贴到第z个元素后面
 * REMOVE 移除根结点
 *
 * 需要的操作:反转、删除、插入、查询区间最小值、成段更新、区间搬移(循环右移转化为区间搬移)
 * 需要的变量:pre,ch,key,size,add,rev,m(最小值)
 */

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define Key_value ch[ch[root][1]][0]//root的右儿子的左儿子
const int MAXN=200010;
const int INF=0x3f3f3f3f;
int pre[MAXN],ch[MAXN][2],key[MAXN],size[MAXN],add[MAXN],rev[MAXN],m[MAXN];
//父结点,儿子,结点权值,子树大小,相关标记
int root,tot1;
int s[MAXN],tot2;//内存池、内存池容量
int a[MAXN];
int n,q;

void NewNode(int &r,int father,int k)
{
    if(tot2)r=s[tot2--];
    else r=++tot1;
    ch[r][0]=ch[r][1]=0;
    pre[r]=father;
    size[r]=1;
    add[r]=rev[r]=0;
    key[r]=m[r]=k;
}
void Update_Rev(int r)
{
    if(!r)return;
    swap(ch[r][0],ch[r][1]);
    rev[r]^=1;
}
void Update_Add(int r,int ADD)
{
    if(!r)return;
    add[r]+=ADD;
    key[r]+=ADD;
    m[r]+=ADD;
}
void Push_Up(int r)
{
    size[r]=size[ch[r][0]]+size[ch[r][1]]+1;
    m[r]=key[r];
    if(ch[r][0])m[r]=min(m[r],m[ch[r][0]]);
    if(ch[r][1])m[r]=min(m[r],m[ch[r][1]]);
}
void Push_Down(int r)
{
    if(rev[r])
    {
        Update_Rev(ch[r][0]);
        Update_Rev(ch[r][1]);
        rev[r]=0;
    }
    if(add[r])
    {
        Update_Add(ch[r][0],add[r]);
        Update_Add(ch[r][1],add[r]);
        add[r]=0;
    }
}
void Build(int &x,int l,int r,int father)
{
    if(l>r)return;
    int mid=(l+r)/2;
    NewNode(x,father,a[mid]);
    Build(ch[x][0],l,mid-1,x);
    Build(ch[x][1],mid+1,r,x);
    Push_Up(x);
}
void Init()
{
    root=tot1=tot2=0;
    ch[root][0]=ch[root][1]=size[root]=add[root]=rev[root]=pre[root]=0;
    m[root]=INF;//这个不用也可以,如果在push_up那判断了的话,否则需要
    NewNode(root,0,INF);
    NewNode(ch[root][1],root,INF);
    Build(Key_value,1,n,ch[root][1]);
    Push_Up(ch[root][1]);
    Push_Up(root);
}
//旋转
void Rotate(int x,int kind)
{
    int y=pre[x];
    Push_Down(y);
    Push_Down(x);
    ch[y][!kind]=ch[x][kind];
    pre[ch[x][kind]]=y;
    if(pre[y])
        ch[pre[y]][ch[pre[y]][1]==y]=x;
    pre[x]=pre[y];
    ch[x][kind]=y;
    pre[y]=x;
    Push_Up(y);
}
//Splay调整
void Splay(int r,int goal)
{
    Push_Down(r);
    while(pre[r]!=goal)
    {
        if(pre[pre[r]]==goal)
        {
            //这题有反转操作,需要先push_down,在判断左右孩子
            Push_Down(pre[r]);
            Push_Down(r);
            Rotate(r,ch[pre[r]][0]==r);
        }

        else
        {
            //这题有反转操作,需要先push_down,在判断左右孩子
            Push_Down(pre[pre[r]]);
            Push_Down(pre[r]);
            Push_Down(r);
            int y=pre[r];
            int kind=(ch[pre[y]][0]==y);
            //两个方向不同,则先左旋再右旋
            if(ch[y][kind]==r)
            {
                Rotate(r,!kind);
                Rotate(r,kind);
            }
            //两个方向相同,相同方向连续两次
            else
            {
                Rotate(y,kind);
                Rotate(r,kind);
            }
        }
    }
    Push_Up(r);
    if(goal==0)root=r;
}
int Get_Kth(int r,int k)
{
    Push_Down(r);
    int t=size[ch[r][0]]+1;
    if(t==k)return r;
    if(t>k)return Get_Kth(ch[r][0],k);
    else return Get_Kth(ch[r][1],k-t);
}
int Get_Min(int r)
{
    Push_Down(r);
    while(ch[r][0])
    {
        r=ch[r][0];
        Push_Down(r);
    }
    return r;
}
int Get_Max(int r)
{
    Push_Down(r);
    while(ch[r][1])
    {
        r=ch[r][1];
        Push_Down(r);
    }
    return r;
}
//下面是操作了
/**
得到区间[l,r]的步骤是,先将l-1结点旋转到root,再将r+1旋转到root的右儿子,那么[l,r]就是root的右儿子的左儿子。
Splay(Get_Kth(root,l),0);
Splay(Get_Kth(root,r+2),root);
*/
void CUT(int l,int r,int c)
{
    Splay(Get_Kth(root,l),0);
    Splay(Get_Kth(root,r+2),root);
    int tmp=Key_value;
    Key_value=0;
    Push_Up(ch[root][1]);
    Push_Up(root);
    Splay(Get_Kth(root,c+1),0);
    Splay(Get_Kth(root,c+2),root);
    Key_value=tmp;
    pre[Key_value]=ch[root][1];
    Push_Up(ch[root][1]);
    Push_Up(root);
}

void Remove()//移除根结点
{
    if(ch[root][0]==0)//没有左孩子
    {
        root=ch[root][1];
        pre[root]=0;
    }
    else
    {
        int m=Get_Max(ch[root][0]);
        Splay(m,root);
        ch[m][1]=ch[root][1];
        pre[ch[root][1]]=m;
        root=m;
        pre[root]=0;
        Push_Up(root);//要更新
    }
}

void ADD(int l,int r,int D)
{
    Splay(Get_Kth(root,l),0);
    Splay(Get_Kth(root,r+2),root);
    Update_Add(Key_value,D);
    Push_Up(ch[root][1]);
    Push_Up(root);
}
void Reverse(int l,int r)
{
    Splay(Get_Kth(root,l),0);
    Splay(Get_Kth(root,r+2),root);
    Update_Rev(Key_value);
    Push_Up(ch[root][1]);
    Push_Up(root);
}
void Revolve(int l,int r,int T)//循环右移
{
    int len=r-l+1;
    T=(T%len+len)%len;
    if(T==0)return;
    int c=r-T+1;//将区间[c,r]放在[l,c-1]前面
    Splay(Get_Kth(root,c),0);
    Splay(Get_Kth(root,r+2),root);
    int tmp=Key_value;
    Key_value=0;
    Push_Up(ch[root][1]);
    Push_Up(root);
    Splay(Get_Kth(root,l),0);
    Splay(Get_Kth(root,l+1),root);
    Key_value=tmp;
    pre[Key_value]=ch[root][1];//这个不用忘记
    Push_Up(ch[root][1]);
    Push_Up(root);
}
void Insert(int x,int P)//在第x个数后面插入P
{
    Splay(Get_Kth(root,x+1),0);
    Splay(Get_Kth(root,x+2),root);
    NewNode(Key_value,ch[root][1],P);
    Push_Up(ch[root][1]);
    Push_Up(root);
}
void erase(int r)//回收内存
{
    if(r)
    {
        s[++tot2]=r;
        erase(ch[r][0]);
        erase(ch[r][1]);
    }
}
void Delete(int x)//删除第x个数
{
    Splay(Get_Kth(root,x),0);
    Splay(Get_Kth(root,x+2),root);
    erase(Key_value);
    pre[Key_value]=0;
    Key_value=0;
    Push_Up(ch[root][1]);
    Push_Up(root);
}
int Query_Min(int l,int r)
{
    Splay(Get_Kth(root,l),0);
    Splay(Get_Kth(root,r+2),root);
    return m[Key_value];
}

void Inorder(int r)
{
    if(!r)return;
    Push_Down(r);
    Inorder(ch[r][0]);
    if(cnt>=1&&cnt<=n)
    {
        printf("%d",key[r]);
        if(cnt<n)printf(" ");
        else printf("\n");
    }
    cnt++;
    Inorder(ch[r][1]);
}

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    char op[20];
    int x,y,z;
    while(scanf("%d",&n)==1)
    {
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        Init();
        scanf("%d",&q);
        while(q--)
        {
            scanf("%s",op);
            if(strcmp(op,"ADD")==0)
            {
                scanf("%d%d%d",&x,&y,&z);
                ADD(x,y,z);
            }
            else if(strcmp(op,"REVERSE")==0)
            {
                scanf("%d%d",&x,&y);
                Reverse(x,y);
            }
            else if(strcmp(op,"REVOLVE")==0)
            {
                scanf("%d%d%d",&x,&y,&z);
                Revolve(x,y,z);
            }
            else if(strcmp(op,"INSERT")==0)
            {
                scanf("%d%d",&x,&y);
                Insert(x,y);
            }
            else if(strcmp(op,"DELETE")==0)
            {
                scanf("%d",&x);
                Delete(x);
            }
            else
            {
                scanf("%d%d",&x,&y);
                printf("%d\n",Query_Min(x,y));
            }
        }
    }
    return 0;
}

/*
 * 维修数列
 * 经典的伸展树的题目。
 * 题目首先给出一个数列,然后进行下列6种操作
 * 1:INSERT post tot c1,c2,...ctot :在当前数列的第pos个数字后插入tot个数字
 * 2:DELETE pos tot : 从当前数列的第pos个数字开始连续 删除tot个数字
 * 3:MAKE-SAME pos tot c :将当前数列的第pos个数字开始连续的tot个数字统一修改为c
 * 4:REVERSE pos tot : 翻转当前数列的第pos个数字来说的连续的tot个数字
 * 5:GET-SUM pos tot :计算当前数列的第pos个数字来说的连续的tot个数字的和并输出
 * 6:MAX-SUM :求出当前数列中和最大的一段序列,输出最大和
 */
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
using namespace std;
#define Key_value ch[ch[root][1]][0]
const int MAXN=500010;
const int INF=0x3f3f3f3f;
int pre[MAXN],ch[MAXN][2],key[MAXN],size[MAXN];
int sum[MAXN],rev[MAXN],same[MAXN];
int lx[MAXN],rx[MAXN],mx[MAXN];
int root,tot1;
int s[MAXN],tot2;
int a[MAXN];
int n,q;
//debug部分
void Treavel(int x)
{
    if(x)
    {
        Treavel(ch[x][0]);
        printf("结点%2d:左儿子 %2d 右儿子 %2d 父结点 %2d key=%2d, size= %2d, sum=%2d,rev=%2d same=%2d lx=%2d rx=%2d mx=%2d\n",x,ch[x][0],ch[x][1],pre[x],key[x],size[x],sum[x],rev[x],same[x],lx[x],rx[x],mx[x]);
        Treavel(ch[x][1]);
    }
}
void debug()
{
    printf("root%d\n",root);
    Treavel(root);
}
void NewNode(int &r,int father,int k)
{
    if(tot2)r=s[tot2--];
    else r=++tot1;
    pre[r]=father;
    ch[r][0]=ch[r][1]=0;
    key[r]=k;
    sum[r]=k;
    rev[r]=same[r]=0;
    lx[r]=rx[r]=mx[r]=k;
    size[r]=1;
}
void Update_Same(int r,int v)
{
    if(!r)return;
    key[r]=v;
    sum[r]=v*size[r];
    lx[r]=rx[r]=mx[r]=max(v,v*size[r]);
    same[r]=1;
}
void Update_Rev(int r)
{
    if(!r)return;
    swap(ch[r][0],ch[r][1]);
    swap(lx[r],rx[r]);
    rev[r]^=1;//这里要注意,一定是异或1
}
void Push_Up(int r)
{
    int lson=ch[r][0],rson=ch[r][1];
    size[r]=size[lson]+size[rson]+1;
    sum[r]=sum[lson]+sum[rson]+key[r];
    lx[r]=max(lx[lson],sum[lson]+key[r]+max(0,lx[rson]));
    rx[r]=max(rx[rson],sum[rson]+key[r]+max(0,rx[lson]));
    mx[r]=max(0,rx[lson])+key[r]+max(0,lx[rson]);
    mx[r]=max(mx[r],max(mx[lson],mx[rson]));
}
void Push_Down(int r)
{
    if(same[r])
    {
        Update_Same(ch[r][0],key[r]);
        Update_Same(ch[r][1],key[r]);
        same[r]=0;
    }
    if(rev[r])
    {
        Update_Rev(ch[r][0]);
        Update_Rev(ch[r][1]);
        rev[r]=0;
    }
}
void Build(int &x,int l,int r,int father)
{
    if(l>r)return;
    int mid=(l+r)/2;
    NewNode(x,father,a[mid]);
    Build(ch[x][0],l,mid-1,x);
    Build(ch[x][1],mid+1,r,x);
    Push_Up(x);
}
void Init()
{
    root=tot1=tot2=0;
    ch[root][0]=ch[root][1]=pre[root]=size[root]=same[root]=rev[root]=sum[root]=key[root]=0;
    lx[root]=rx[root]=mx[root]=-INF;
    NewNode(root,0,-1);
    NewNode(ch[root][1],root,-1);
    for(int i=0;i<n;i++)scanf("%d",&a[i]);
    Build(Key_value,0,n-1,ch[root][1]);
    Push_Up(ch[root][1]);
    Push_Up(root);
}
void Rotate(int x,int kind)
{
    int y=pre[x];
    Push_Down(y);
    Push_Down(x);
    ch[y][!kind]=ch[x][kind];
    pre[ch[x][kind]]=y;
    if(pre[y])
        ch[pre[y]][ch[pre[y]][1]==y]=x;
    pre[x]=pre[y];
    ch[x][kind]=y;
    pre[y]=x;
    Push_Up(y);
}
void Splay(int r,int goal)
{
    Push_Down(r);
    while(pre[r]!=goal)
    {
        if(pre[pre[r]]==goal)
        {
            Push_Down(pre[r]);
            Push_Down(r);
            Rotate(r,ch[pre[r]][0]==r);
        }
        else
        {
            Push_Down(pre[pre[r]]);
            Push_Down(pre[r]);
            Push_Down(r);
            int y=pre[r];
            int kind=ch[pre[y]][0]==y;
            if(ch[y][kind]==r)
            {
                Rotate(r,!kind);
                Rotate(r,kind);
            }
            else
            {
                Rotate(y,kind);
                Rotate(r,kind);
            }
        }
    }
    Push_Up(r);
    if(goal==0)root=r;
}
int Get_Kth(int r,int k)
{
    Push_Down(r);
    int t=size[ch[r][0]]+1;
    if(t==k)return r;
    if(t>k)return Get_Kth(ch[r][0],k);
    else return Get_Kth(ch[r][1],k-t);
}

//在第pos个数后插入tot个数
void Insert(int pos,int tot)
{
    for(int i=0;i<tot;i++)scanf("%d",&a[i]);
    Splay(Get_Kth(root,pos+1),0);
    Splay(Get_Kth(root,pos+2),root);
    Build(Key_value,0,tot-1,ch[root][1]);
    Push_Up(ch[root][1]);
    Push_Up(root);
}
void erase(int r)
{
    if(!r)return;
    s[++tot2]=r;
    erase(ch[r][0]);
    erase(ch[r][1]);
}
//从第pos个数开始连续删除tot个数
void Delete(int pos,int tot)
{
    Splay(Get_Kth(root,pos),0);
    Splay(Get_Kth(root,pos+tot+1),root);
    erase(Key_value);
    pre[Key_value]=0;
    Key_value=0;
    Push_Up(ch[root][1]);
    Push_Up(root);
}
//从第pos个数连续开始的tot个数修改为c
void Make_Same(int pos,int tot,int c)
{
    Splay(Get_Kth(root,pos),0);
    Splay(Get_Kth(root,pos+tot+1),root);
    Update_Same(Key_value,c);
    Push_Up(ch[root][1]);
    Push_Up(root);
}
//反转
void Reverse(int pos,int tot)
{
    Splay(Get_Kth(root,pos),0);
    Splay(Get_Kth(root,pos+tot+1),root);
    Update_Rev(Key_value);
    Push_Up(ch[root][1]);
    Push_Up(root);
}
//求和
int Get_Sum(int pos,int tot)
{
    Splay(Get_Kth(root,pos),0);
    Splay(Get_Kth(root,pos+tot+1),root);
    return sum[Key_value];
}
//得到最大和
int Get_MaxSum(int pos,int tot)
{
    Splay(Get_Kth(root,pos),0);
    Splay(Get_Kth(root,pos+tot+1),root);
    return mx[Key_value];
}
void Inorder(int r)
{
    if(!r)return;
    Push_Down(r);
    Inorder(ch[r][0]);
    printf("%d ",key[r]);
    Inorder(ch[r][1]);
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    while(scanf("%d%d",&n,&q)==2)
    {
        Init();
        char op[20];
        int x,y,z;
        while(q--)
        {
            scanf("%s",op);
            if(op[0]=='I')
            {
                scanf("%d%d",&x,&y);
                Insert(x,y);
            }
            else if(op[0]=='D')
            {
                scanf("%d%d",&x,&y);
                Delete(x,y);
            }
            else if(op[0]=='M'&&op[2]=='K')
            {
                scanf("%d%d%d",&x,&y,&z);
                Make_Same(x,y,z);
            }
            else if(op[0]=='R')
            {
                scanf("%d%d",&x,&y);
                Reverse(x,y);
            }
            else if(op[0]=='G')
            {
                scanf("%d%d",&x,&y);
                printf("%d\n",Get_Sum(x,y));
            }
            else
            {
                printf("%d\n",Get_MaxSum(1,size[root]-2));
            }
        }
    }
    return 0;
}

树分治

引入

/*题目:给一棵边权无根树,问是否有点对距离为k
思路:暴力枚举所有点对,复杂度O(n^2logn)
有性质:任意点对的距离,
要么经过根节点
要么不经过根结点
经过根结点时 dis(u,v) = dis(u,rt) + dis(rt,v)
不经过时,我们递归讨论。
如果递归层数过大,复杂度任然爆炸。
有结论:找到树的重心后,均摊复杂度nlogn。*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
/**
设根为rt,子树为v1..vi,求出vi中每个结点到rt的距离保存到rem
pd[dis]表示在v1..vi-1有没有到rt距离为dis的
那么如果rem[x]+pd[dis]满足,则表示该询问的路径存在
将所有子树清空后清空数组
**/
int n,m;
int rt,sum,f[maxn];
int head[maxn],ecnt;
struct edge{int to,nxt,w;}e[maxn<<1];
void adde(int u,int v,int w){
    e[++ecnt]={v,head[u],w};head[u]=ecnt;
}
int qr[maxn],vs[maxn];
int vis[maxn],siz[maxn];
int dis[maxn];
int pd[maxn];
void getroot(int u,int pre){
    f[u]=0,siz[u]=1;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v==pre||vis[v])continue;
        getroot(v,u);
        siz[u]+=siz[v];
        f[u]=max(f[u],siz[v]);
    }f[u]=max(f[u],sum-siz[u]);
    if(f[u]<f[rt])rt=u;
}
int rem[maxn],q[maxn];
void getdis(int u,int fa){
    rem[++rem[0]]=dis[u];
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v==fa||vis[v])continue;
        dis[v]=dis[u]+e[i].w;
        getdis(v,u);
    }
}
void calc(int u){
    int p=0;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(vis[v])continue;
        rem[0]=0;dis[v]=e[i].w;
        getdis(v,u);//处理每个子树的dis
        for(int j=rem[0];j;--j){
            for(int k=1;k<=m;++k){
                if(qr[k]>=rem[j])vs[k]|=pd[qr[k]-rem[j]];
                //如果存在,标记答案
            }
        }
        for(int j=rem[0];j;--j){
            q[++p]=rem[j],pd[rem[j]]=1;//把出现过的dis保存到pd里
        }
    }
    for(int i=1;i<=p;++i)pd[q[i]]=0;//清空pd
}
void solve(int u){
    vis[u]=pd[0]=1;calc(u);//pd[i]表示到根距离为i的路径是否存在
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(vis[v])continue;
        f[0]=n;sum=siz[v];rt=0;
        getroot(v,0);solve(rt);//在子树找到重心并递归处理
    }
}
int main(){
    scanf("%d%d",&n,&m);
    f[0]=sum=n;
    int u,v,w;
    for(int i=2;i<=n;++i){
        scanf("%d%d%d",&u,&v,&w);adde(u,v,w);adde(v,u,w);
    }
    for(int i=1;i<=m;++i)scanf("%d",qr+i);//离线查询
    rt=0;getroot(1,0);solve(rt);//找整个树的重心,然后分治
    for(int i=1;i<=m;++i)puts(vs[i]?"AYE":"NAY");
    return 0;
}

有多少点对距离不大于k

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 10005;
struct edge{int to,next,w;}a[N<<1];
int n,m,k,head[N],cnt,root,sum,vis[N],sz[N],f[N],dep[N],o[N],ans;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
void getroot(int u,int fa){
    sz[u]=1;f[u]=0;
    for (int e=head[u];e;e=a[e].next){
        int v=a[e].to;if (v==fa||vis[v]) continue;
        getroot(v,u);
        sz[u]+=sz[v];
        f[u]=max(f[u],sz[v]);
    }
    f[u]=max(f[u],sum-sz[u]);
    if (f[u]<f[root]) root=u;
}
void getdeep(int u,int fa){
    o[++cnt]=dep[u];
    for (int e=head[u];e;e=a[e].next){
        int v=a[e].to;if (v==fa||vis[v]) continue;
        dep[v]=dep[u]+a[e].w;getdeep(v,u);
    }
}
int calc(int u,int d0){
    cnt=0;dep[u]=d0;
    getdeep(u,0);
    sort(o+1,o+cnt+1);
    int l=1,r=cnt,res=0;
    while (l<r)
        if (o[l]+o[r]<=k) res+=r-l,l++;
        else r--;
    return res;
}
void solve(int u){
    ans+=calc(u,0);
    vis[u]=1;
    for (int e=head[u];e;e=a[e].next){
        int v=a[e].to;if (vis[v]) continue;
        ans-=calc(v,a[e].w);
        sum=sz[v];root=0;
        getroot(v,0);
        solve(root);
    }
}
int main(){
    while (1){
        n=gi();k=gi();
        if (n==0&&k==0) return 0;
        memset(head,0,sizeof(head));
        memset(vis,0,sizeof(vis));
        cnt=0;ans=0;
        for (int i=1,u,v,w;i<n;i++){
            u=gi();v=gi();w=gi();
            a[++cnt]=(edge){v,head[u],w};head[u]=cnt;
            a[++cnt]=(edge){u,head[v],w};head[v]=cnt;
        }
        root=0;sum=f[0]=n;
        getroot(1,0);solve(root);
        printf("%d\n",ans);
    }
}

距离为3的倍数的点对数

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e4+10;

int n;
int rt,sum,f[maxn];
int head[maxn],ecnt;
struct edge{int to,nxt,w;}e[maxn<<1];
void adde(int u,int v,int w){
    e[++ecnt]={v,head[u],w};head[u]=ecnt;
}
int vis[maxn],dis[maxn],pd[5],siz[maxn];
void getroot(int u,int pre){
    f[u]=0;siz[u]=1;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v==pre||vis[v])continue;
        getroot(v,u);
        siz[u]+=siz[v];
        f[u]=max(f[u],siz[v]);
    }f[u]=max(f[u],sum-siz[u]);
    if(f[u]<f[rt])rt=u;
}
void getdis(int u,int fa){
    ++pd[dis[u]];
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v==fa||vis[v])continue;
        (dis[v]=dis[u]+e[i].w)%=3;
        getdis(v,u);
    }
}
int calc(int u,int w){
    memset(pd,0,sizeof pd);
    dis[u]=w;
    getdis(u,0);
    return 2*pd[1]*pd[2] + pd[0]*pd[0];
}
int ans = 0;
void solve(int u){
    ans += calc(u,0);
    vis[u]=1;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(vis[v])continue;
        ans -= calc(v,e[i].w);
        rt=0;sum=siz[v];getroot(v,u);solve(rt);
    }
}
int main(){
    scanf("%d",&n);
    int u,v,w;
    for(int i=1;i<n;++i){
        scanf("%d%d%d",&u,&v,&w);w%=3;
        adde(u,v,w);adde(v,u,w);
    }
    sum=f[0]=n;rt=0;
    getroot(1,0);
    solve(rt);
    int fm = n*n;
    int gc = __gcd(fm,ans);
    ans/=gc;fm/=gc;
    printf("%d/%d",ans,fm);
    return 0;
}

CDQ分治

2018湖南省赛,线段覆盖问题。

#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;

const int maxn = 1e5+10;
struct Node{
    int idx,l,r,delt;
    int kind;
    bool operator < (const Node &rhs)const{
        return l<rhs.l;
    }
}node[maxn];

int ans[maxn],del[maxn];
/*******反向BIT*******/
inline int lowbit(int x){return x&(-x);}
int arr[maxn],MAX;
void add(int x,int d){
    while(x){
        arr[x]+=d;
        x-=lowbit(x);
    }
}
int sum(int x){
    int ret=0;
    while(x<=MAX){
        ret+=arr[x];
        x+=lowbit(x);
    }return ret;
}
/****离散化****/
int vec[maxn],vec_idx;
int hs(int x){
    return lower_bound(vec,vec+vec_idx,x)-vec+1;
}
/*****CDQ*****/
void cdq(int l,int r){
    if(l==r)return;
    int mid = (l+r)>>1;
    cdq(l,mid);
    cdq(mid+1,r);
    int j=l;
    for(int i=mid+1;i<=r;i++){
        if(node[i].kind==2){
            for(;j<=mid && node[j].l<=node[i].l;j++){
                if(node[j].kind==1){
                    add(hs(node[j].r),node[j].delt);
                }
            }
            ans[node[i].idx]+=sum(hs(node[i].r));
        }
    }
    for(int i=l;i<j;i++){
        if(node[i].kind==1){
            add(hs(node[i].r),-node[i].delt);
        }
    }
    inplace_merge(node+l,node+mid+1,node+r+1);
}
int vis[maxn];
int main(){
    int n,m;
    while(scanf("%d%d",&m,&n)!=EOF){
        int cnt=0;
        vec_idx=0;
        memset(arr,0,sizeof(arr));
        memset(ans,0,sizeof(ans));
        memset(vis,0,sizeof(vis));
        vector<int> v;
        char op[3];
        for(int i=1;i<=n;i++){
            scanf("%s",op);
            if(op[0]=='1'){
                scanf("%d%d",&node[i].l,&node[i].r);
                node[i].kind=1;
                node[i].idx=i;
                node[i].delt=1;
                vec[vec_idx++]=node[i].r;
                v.push_back(i);
            } else if(op[0]=='2'){
                scanf("%d%d",&node[i].l,&node[i].r);
                node[i].kind=2;
                node[i].idx=i;
                vec[vec_idx++]=node[i].r;
                vis[i]=1;
            } else{
                int tmp;
                scanf("%d",&tmp);
                node[i].kind=1;
                node[i].l=node[v[tmp-1]].l;
                node[i].r=node[v[tmp-1]].r;
                node[i].delt=-1;
                node[i].idx=i;
            }
        }
        sort(vec,vec+vec_idx);
        MAX = vec_idx+10;
        cdq(1,n);
        for(int i=1;i<=n;i++){
            if(vis[i]){
                printf("%d\n",ans[i]);
            }
        }
    }
    return 0;
}

三维偏序

/*
给定N个三维的点,问对于点i,
有多少个点三个坐标都小于点i(1<=i<=n)
*/
using namespace std;
#define N 100010
struct rec{
    int x,y,z,id;
    bool operator==(const rec &a){
        return x==a.x&&y==a.y&&z==a.z;
    }
}a[N],tmp[N];
int ans[N],c[N];
int n;
bool cmp1(rec &a,rec &b){
    if(a==b) return a.id<b.id;
    return a.x<b.x||a.x==b.x&&a.y<b.y||a.x==b.x&&a.y==b.y&&a.z<b.z;
}
bool cmp2(rec &a,rec &b){
    return a.y<b.y||a.y==b.y&&a.id<b.id;
}
inline void add(int x,int w){
    for(;x<=100000;x+=x&-x) c[x]+=w;
}
inline int getsum(int x){
    int ans=0;
    for(;x;x-=x&-x) ans+=c[x];
    return ans;
}
void solve(int l,int r){
    if(l==r) return;
    int mid=l+r>>1;
    for(int i=l;i<=r;++i) tmp[i]=a[i];
    sort(a+l,a+mid+1,cmp2);
    sort(a+mid+1,a+r+1,cmp2);
    int ll=l;
    for(int i=mid+1;i<=r;++i){
        while(ll<=mid&&a[ll].y<=a[i].y) add(a[ll++].z,1);
        ans[a[i].id]+=getsum(a[i].z);
    }
    for(int i=l;i<ll;++i) add(a[i].z,-1);
    for(int i=l;i<=r;++i) a[i]=tmp[i];
    solve(l,mid);solve(mid+1,r);
}
int main(){
    int ca;
    scanf("%d",&ca);
    while(ca--){
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
            a[i].id=i;
        }
        sort(a+1,a+n+1,cmp1);
        memset(ans,0,sizeof(ans));
        solve(1,n);
        for(int i=n-1;i>0;--i)
            if(a[i]==a[i+1]) ans[a[i].id]=ans[a[i+1].id];
        for(int i=1;i<=n;++i)
            printf("%d\n",ans[i]);
    }
    return 0;
}

三维LIS

/*
给定n个三维坐标,求其LIS及其数量,
当x1<=x2,y1<=y2,z1<=z2是,p1<=p2。
先排序掉一维,然后剩下两维分治,
用一个带长度和方案数的结构体树状数组维护。
*/
using namespace std;
#define N 100010
const int mod=(1<<30)-1;
struct Point{
    int x,y,z,id;
}q[N],tmp[N];
struct rec{
    int len,num;
    void init() {
        len=num=0;
    }
}dp[N],c[N];
int a[N];
int n,tot;
void updata(rec &a,rec b){
    if(a.len<b.len) a=b;
    else if(a.len==b.len) a.num+=b.num,a.num&=mod;
}
void add(int x,const rec &a){
    for(;x<=tot;x+=x&-x) updata(c[x],a);
}
rec getmax(int x){
    rec ans;ans.init();
    for(;x;x-=x&-x) updata(ans,c[x]);
    return ans;
}
void clr(int x){
    for(;x<=tot;x+=x&-x) c[x].init();
}
bool cmp1(Point &p1,Point &p2){
    return p1.x<p2.x||p1.x==p2.x&&p1.y<p2.y||p1.x==p2.x&&p1.y==p2.y&&p1.z<p2.z;
}
bool cmp2(Point &p1,Point &p2){
    return p1.y<p2.y||p1.y==p2.y&&p1.id<p2.id;
}
void solve(int s,int t){
    if(s==t) return;
    int mid=s+t>>1;
    solve(s,mid);
    for(int i=s;i<=t;++i) tmp[i]=q[i];
    sort(tmp+s,tmp+t+1,cmp2);
    rec ans;
    for(int i=s;i<=t;++i)
        if(tmp[i].id<=mid)
            add(tmp[i].z,dp[tmp[i].id]);
        else{
            ans=getmax(tmp[i].z);++ans.len;
            updata(dp[tmp[i].id],ans);
        }
    for(int i=s;i<=t;++i)
        if(tmp[i].id<=mid) clr(tmp[i].z);
    solve(mid+1,t);
}
int main(){
    int ca;
    scanf("%d",&ca);
    while(ca--){
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%d%d%d",&q[i].x,&q[i].y,&q[i].z);
            a[i]=q[i].z;dp[i].len=dp[i].num=1;
        }
        sort(a+1,a+n+1);
        tot=unique(a+1,a+n+1)-a-1;
        for(int i=1;i<=n;++i)
            q[i].z=lower_bound(a+1,a+tot+1,q[i].z)-a;
        sort(q+1,q+n+1,cmp1);
        for(int i=1;i<=n;++i) q[i].id=i;
        solve(1,n);
        rec ans;ans.init();
        for(int i=1;i<=n;++i) updata(ans,dp[i]);
        printf("%d %d\n",ans.len,ans.num);
    }
    return 0;
}

长方体内点的数量

/*
1 x y z 添加一个点
2 x1 y1 z1 x2 y2 z2 查找长方体内点的数量
CDQ分治套CDQ分治套树状数组
第一重CDQ分治计算左边的修改对右边的询问的影响,
第二重CDQ分治处理三维偏序问题
*/
using namespace std;
#define N 50010
struct rec{
    int k,x,y,z,w,id;
    rec(){}
    rec(int k,int x,int y,int z,int w,int id):
        k(k),x(x),y(y),z(z),w(w),id(id){}
    bool operator==(const rec &a){
        return x==a.x&&y==a.y&&z==a.z;
    }
}q[N*8],qq[N*8],tmp[N*8];
int ans[N],tree[N*2],a[N*2],b[N*2],c[N*2],kind[N];
int n,ta,tb,tc;
bool cmp1(rec &a,rec &b){
    if(a==b) return a.id<b.id;
    return a.x<b.x||a.x==b.x&&a.y<b.y||a.x==b.x&&a.y==b.y&&a.z<b.z;
}
bool cmp2(rec &a,rec &b){
    return a.y<b.y;
}
inline void add(int x,int w){
    for(;x<=tc;x+=x&-x) tree[x]+=w;
}
inline int getsum(int x){
    int ans=0;
    for(;x;x-=x&-x) ans+=tree[x];
    return ans;
}
void cdq(int l,int r){
    if(l==r) return;
    int mid=l+r>>1;
    for(int i=l;i<=r;++i) tmp[i]=qq[i];
    sort(qq+l,qq+mid+1,cmp2);
    sort(qq+mid+1,qq+r+1,cmp2);
    int ll=l;
    for(int i=mid+1;i<=r;++i){
        while(ll<=mid&&qq[ll].y<=qq[i].y){
            if(qq[ll].k==1) add(qq[ll].z,1);
            ++ll;
        }
        if(qq[i].k==2) ans[qq[i].id]+=getsum(qq[i].z)*qq[i].w;
    }
    for(int i=l;i<ll;++i)
        if(qq[i].k==1) add(qq[i].z,-1);
    for(int i=l;i<=r;++i) qq[i]=tmp[i];
    cdq(l,mid);cdq(mid+1,r);
}
void solve(int l,int r){
    if(l==r) return;
    int mid=l+r>>1,n=0;
    for(int i=l;i<=mid;++i)
        if(q[i].k==1) qq[++n]=q[i];
    for(int i=mid+1;i<=r;++i)
        if(q[i].k==2) qq[++n]=q[i];
    if(n){
        sort(qq+1,qq+n+1,cmp1);
        cdq(1,n);
    }
    solve(l,mid);solve(mid+1,r);
}
int main(){
    int ca;
    scanf("%d",&ca);
    while(ca--){
        scanf("%d",&n);
        int nn=0,k,a1,b1,c1,a2,b2,c2;
        ta=tb=tc=0;
        for(int i=1;i<=n;++i){
            scanf("%d%d%d%d",&k,&a1,&b1,&c1);
            kind[i]=k;
            if(k==1){
                q[++nn]=rec(k,a1,b1,c1,1,i);
                c[++tc]=c1;
            }
            else{
                scanf("%d%d%d",&a2,&b2,&c2);
                --a1;--b1;--c1;
                c[++tc]=c1;c[++tc]=c2;
                q[++nn]=rec(k,a2,b2,c2,1,i);
                q[++nn]=rec(k,a1,b2,c2,-1,i);
                q[++nn]=rec(k,a2,b1,c2,-1,i);
                q[++nn]=rec(k,a2,b2,c1,-1,i);
                q[++nn]=rec(k,a1,b1,c2,1,i);
                q[++nn]=rec(k,a1,b2,c1,1,i);
                q[++nn]=rec(k,a2,b1,c1,1,i);
                q[++nn]=rec(k,a1,b1,c1,-1,i);
            }
        }
        sort(c+1,c+tc+1);tc=unique(c+1,c+tc+1)-c-1;
        for(int i=1;i<=nn;++i)
            q[i].z=lower_bound(c+1,c+tc+1,q[i].z)-c;
        memset(ans,0,sizeof(ans));
        solve(1,nn);
        for(int i=1;i<=n;++i)
            if(kind[i]==2) printf("%d\n",ans[i]);
    }
    return 0;
}

可持久化并查集

#include <iostream>
#include <algorithm>
#include <stdio.h>
using namespace std;
typedef int LL;
#define dd c=getchar()
inline LL read(){LL a=0,b=1;char dd;while(!isdigit(c)&&c!='-')dd;
    if(c=='-'){b=-b;dd;}while(isdigit(c)){a=a*10+c-'0';dd;}return a*b;}
#undef dd
template <int n>
class vervec{
LL tree[20*n],ls[20*n],rs[20*n],hd[n],hd1[2*n],tt,t,nh;LL*a;
LL&ins(LL nod,LL l,LL r,LL x){/**  **/
    if(l==r){return tree[nod];}
    int mid=l+r>>1;
    if(x<=mid){tree[++tt]=tree[ls[nod]];ls[tt]=ls[ls[nod]];rs[tt]=rs[ls[nod]];
        ls[nod]=tt;return ins(tt,l,mid,x);}
    if(x>mid){tree[++tt]=tree[rs[nod]];ls[tt]=ls[rs[nod]];rs[tt]=rs[rs[nod]];
        rs[nod]=tt;return ins(tt,mid+1,r,x);}
}
void chit(){LL x=hd[nh];hd[++t]=++tt;tree[tt]=tree[x];
    ls[tt]=ls[x];rs[tt]=rs[x];nh=t;}
int build(LL l,LL r){/**主席树建树**/
    LL nod=++tt;if(l==r){tree[nod]=a[l];ls[nod]=rs[nod]=0;return nod;}
    LL mid=l+r>>1;
    ls[nod]=build(l,mid);
    rs[nod]=build(mid+1,r);
    return nod;
}
void init(){tt=0;hd[nh=0]=1;build(1,n);}/**清空并建树**/
LL query(LL nod,LL l,LL r,LL x){
    if(l==r)return tree[nod];
    LL mid=l+r>>1;
    if(x<=mid)return query(ls[nod],l,mid,x);
    else if(x>mid)return query(rs[nod],mid+1,r,x);
}
public:
    vervec(){tt=0;t=0;nh=1;}
    void init(LL*x){a=x;init();}
    void mark(LL x){hd1[x]=nh;}/**修改当前时间戳**/
    LL&operator[](LL x){chit();return ins(hd[nh],1,n,x);}
    LL val(LL x){return query(hd[nh],1,n,x);}
    void rever(LL x){nh=hd1[x];}
};
vervec<120003>f,c;
LL getf(LL v){
    LL fa=f.val(v);
    if(fa==v)return v;
    return getf(fa);
}
void add(LL a,LL b){/**启发式合并**/
    a=getf(a);
    b=getf(b);
    if(a==b)return;
    LL va=c.val(a),vb=c.val(b);
    if(va==vb){
        f[a]=b;
        c[b]++;
    }else if(va<vb){
        f[a]=b;
    }else{
        f[b]=a;
    }
}
LL sol[120003],n,m,x,y,z;
void init(LL n){
    for(LL i=1;i<=n;i++)sol[i]=i;/****初始集合为i*****/
    f.init(sol);                 /**建树**/
    for(LL i=1;i<=n;i++)sol[i]=1;
    c.init(sol);
}
int main(){
    n=read();
    m=read();           /***n个集合,m个操作***/
    init(n);
    f.mark(0);
    c.mark(0);
    for(LL i=1;i<=m;i++){
        x=read();
        if(x==1){       /***合并两个集合***/
            x=read();
            y=read();
            add(x,y);
        }else if(x==2){ /***回退到第k次操作的状态***/
            y=read();
            f.rever(y);
            c.rever(y);
        }else{          /*****询问两个集合是否是同一个集合******/
            x=read();
            y=read();
            puts(getf(x)==getf(y)?"1":"0");
        }
        f.mark(i);
        c.mark(i);
    }
    return 0;
}

带撤回的并查集

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010;
//union find
int f[N];
void INIT(int size)
{ for(int i=0;i<=size;i++) f[i]=i; }
int findf(int x)
{ while(f[x]!=x) x=f[x]; return x; }
void setroot(int x)
{ if(f[x]!=x) setroot(f[x]); f[x]=f[f[x]]=x; }
void link(int a,int b)
{ setroot(a); f[a]=b; }
void cut(int a,int b)
{ setroot(a); f[b]=b; }
char op[10];
int main(){
    int n,a,b;
    scanf("%d",&n);
    INIT(n);
    while(scanf("%s",op)&&op[0]!='E') {
        scanf("%d%d",&a,&b);
        if(op[0]=='C') link(a,b);
        else if(op[0]=='D')cut(a,b);
        else {
            if(findf(a)==findf(b))puts("YES");
            else puts("NO");
            fflush(stdout);
        }
    }
    return 0;
}

整体二分

静态区间第k小

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 100010
const int inf=1e9;
struct rec{
    int w,id;
    rec(){}
    rec(int w,int id):w(w),id(id){}
    bool operator<(const rec a)const
    {
        return w<a.w;
    }
}a[N];
struct interval{
    int l,r,k,id,cnt;
}q[N],tmp[N];
int n,m;
int c[N],ans[N];
inline void add(int x,int w)
{
    for(;x<=n;x+=x&-x) c[x]+=w;
}
inline int getsum(int x)
{
    int ans=0;
    for(;x;x-=x&-x) ans+=c[x];
    return ans;
}
void calc(int s,int t,int l,int r)
{
    int x=lower_bound(a+1,a+n+1,rec(l,0))-a;
    for(int i=x;i<=n&&a[i].w<=r;++i) add(a[i].id,1);
    for(int i=s;i<=t;++i)
        q[i].cnt=getsum(q[i].r)-getsum(q[i].l-1);
    for(int i=x;i<=n&&a[i].w<=r;++i) add(a[i].id,-1);
}
void solve(int s,int t,int l,int r)
{
    if(l==r)
    {
        for(int i=s;i<=t;++i) ans[q[i].id]=l;
        return;
    }
    int mid=l+(r-l>>1);
    calc(s,t,l,mid);
    int ss=s-1,tt=t+1;
    for(int i=s;i<=t;++i)
    {
        if(q[i].cnt>=q[i].k) tmp[++ss]=q[i];
        else q[i].k-=q[i].cnt,tmp[--tt]=q[i];
    }
    for(int i=s;i<=t;++i) q[i]=tmp[i];
    if(ss>=s) solve(s,ss,l,mid);
    if(tt<=t) solve(tt,t,mid+1,r);
}
bool cmp(rec a,rec b)
{
    return a.w<b.w;
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&a[i].w);a[i].id=i;
        }
        sort(a+1,a+n+1,cmp);
        for(int i=1;i<=m;++i)
        {
            scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].k);
            q[i].cnt=0;q[i].id=i;
        }
        solve(1,m,-inf,inf);
        for(int i=1;i<=m;++i) printf("%d\n",ans[i]);
    }
    return 0;
}

动态区间第k小

using namespace std;
#define N 100010
struct rec{
    int x,y,k,cnt,id;
    rec(){}
    rec(int x,int y,int k,int cnt,int id):
        x(x),y(y),k(k),cnt(cnt),id(id){}
}q[N*3],q1[N*3],q2[N*3];
const int inf=1e9;
int a[N],ans[N*3],c[N];
int n,m,nn;
inline void add(int x,int w){
    for(;x<=n;x+=x&-x) c[x]+=w;
}
inline int getsum(int x){
    int ans=0;
    for(;x;x-=x&-x) ans+=c[x];
    return ans;
}
void calc(int s,int t,int l,int r){
    for(int i=s;i<=t;++i){
        if(q[i].k) q[i].cnt=getsum(q[i].y)-getsum(q[i].x-1);
        else if(q[i].y<=r) add(q[i].x,q[i].cnt);
    }
    for(int i=s;i<=t;++i)
        if(!q[i].k&&q[i].y<=r) add(q[i].x,-q[i].cnt);
}
void solve(int s,int t,int l,int r){
    if(l==r){
        for(int i=s;i<=t;++i)
            if(q[i].k) ans[q[i].id]=l;
        return;
    }
    int mid=l+(r-l>>1);
    calc(s,t,l,mid);
    int t1=0,t2=0;
    for(int i=s;i<=t;++i){
        if(q[i].k){
            if(q[i].cnt>=q[i].k) q1[++t1]=q[i];
            else q[i].k-=q[i].cnt,q2[++t2]=q[i];
        }
        else{
            if(q[i].y<=mid) q1[++t1]=q[i];
            else q2[++t2]=q[i];
        }
    }
    for(int i=1;i<=t1;++i) q[s+i-1]=q1[i];
    for(int i=1;i<=t2;++i) q[t-t2+i]=q2[i];
    if(t1) solve(s,s+t1-1,l,mid);
    if(t2) solve(s+t1,t,mid+1,r);
}
int main(){
    while(scanf("%d",&n)!=EOF){
        for(int i=1;i<=n;++i){
            scanf("%d",&a[i]);
            q[i]=rec(i,a[i],0,1,i);
            ans[i]=0;
        }
        nn=n;
        scanf("%d",&m);
        int x,y,k,z;
        for(int i=1;i<=m;++i){
            scanf("%d%d%d",&z,&x,&y);
            if(z==1){
                ++nn;q[nn]=rec(x,a[x],0,-1,nn);
                ++nn;q[nn]=rec(x,y,0,1,nn);
                a[x]=y;
                ans[nn]=0;
            }
            else {
                scanf("%d",&k);
                ++nn;q[nn]=rec(x,y,k,0,nn);
            }
        }
        memset(ans,0,sizeof(ans));
        solve(1,nn,1,inf);
        for(int i=1;i<=nn;++i)
            if(ans[i]) printf("%d\n",ans[i]);
    }
    return 0;
}

笛卡尔树

/**
笛卡尔树模板
1.值域是堆,下标是二叉搜索树
2.中序遍历就是原序列,构造O(n)
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
int stk[maxn],tp;
void build(int n,int a[],int ch[][2],int fa[]){
    tp=0;
    for(int i=1;i<=n;++i){
        int last=0;
        while(tp){
            if(a[stk[tp]]<=a[i]){
                if(ch[stk[tp]][1])
                    ch[i][0]=ch[stk[tp]][1],fa[ch[stk[tp]][1]]=i;
                ch[stk[tp]][1]=i;fa[i]=stk[tp];
                break;
            }last=stk[tp--];
        }
        if(!tp&&last)ch[i][0]=last,fa[last]=i;
        stk[++tp]=i;
    }
}
int n,a[maxn],b[maxn],fa[maxn],ch[maxn][2],faa[maxn],chh[maxn][2];
bool d(int x){return ch[fa[x]][1]==x;}
int query(int x,int v){while(fa[x]&&d(x)==v)x=fa[x];return fa[x];}
void dfs(int rt){
    if(!a[rt])return;
    dfs(ch[rt][0]);
    cout<<fa[rt]<<" "<<a[rt]<<endl;
    dfs(ch[rt][1]);
}

bool ok(int len){
    for(int i=1;i<=len;++i){
        fa[i]=faa[i]=0;ch[i][0]=ch[i][1]=0;
        chh[i][0]=chh[i][1]=0;
    }
    build(len,a,ch,fa);
    build(len,b,chh,faa);
    for(int i=1;i<=len;++i){
        if(fa[i]!=faa[i])return 0;
        if(ch[i][0]!=chh[i][0])return 0;
        if(ch[i][1]!=chh[i][1])return 0;
    }return 1;
}
int main(){
    while(cin>>n){
        for(int i=1;i<=n;i++)scanf("%d",a+i);
        for(int i=1;i<=n;i++)scanf("%d",b+i);
        int low=0,high=n+1;
        while(high-low>1){
            int mid=low+high>>1;
            if(ok(mid))low=mid;
            else high=mid;
        }cout<<low<<endl;
    }
    return 0;
}

字典树

01 trie

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
int n,head[maxn],cnt,val[maxn];
struct edge{
    int nxt,to,w;
}e[maxn<<1];
void adde(int u,int v,int w){
    e[++cnt].nxt=head[u];
    e[cnt].to=v;
    e[cnt].w=w;
    head[u]=cnt;
}
void dfs(int u,int pre,int v){
    val[u]=v;
    for(int i=head[u];i;i=e[i].nxt){
        if(e[i].to==pre)continue;
        dfs(e[i].to,u,v^e[i].w);
    }
}
int ans=0;
int son[maxn*30+5][2],tot;
void ask(int x){
    int u=0,tmp=0;
    for(int i=30;i>=0;i--){
        bool c=(((1<<i)&x)!=0);
        if(son[u][!c]){
            tmp|=(1<<i);u=son[u][!c];
        } else {
            u=son[u][c];
        }
    }ans=max(ans,tmp);
}
void ins(int x){
    int u=0;
    for(int i=30;i>=0;i--){
        bool c=(((1<<i)&x)!=0);//当前这位是1还是0
        if(!son[u][c])son[u][c]=++tot;//出没出现过,没出现过就新建节点
        u=son[u][c];
    }
}
int main(){
    scanf("%d",&n);
    int u,v,w;
    for(int i=1;i<n;i++){
        scanf("%d%d%d",&u,&v,&w);
        adde(u,v,w);adde(v,u,w);
    }
    dfs(1,0,0);
    for(int i=1;i<=n;i++){
        ask(val[i]);ins(val[i]);
    }cout<<ans;
    return 0;
}

可持久化trie树

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 6e5+10;
int las=0;
int n,m,son[maxn*24*2][2],siz[maxn*24*2],tot,ttot;
int T[maxn];
void ins(int x){
    int rt=T[ttot];
    T[++ttot]=++tot;
    int nrt=tot;
    for(int i=24;i>=0;i--){
        bool c=(x>>i)&1;
        son[nrt][c]=++tot;
        son[nrt][!c]=son[rt][!c];
        rt=son[rt][c];nrt=son[nrt][c];
        siz[nrt]=siz[rt]+1;
    }
}
int ask(int lrt,int rrt,int x){
    int ret=0;
    for(int i=24;i>=0;i--){
        bool c=(x>>i)&1;
        if(siz[son[rrt][!c]] - siz[son[lrt][!c]] >0 ){
            ret|=(1<<i);
            lrt=son[lrt][!c];rrt=son[rrt][!c];
        } else {
            lrt=son[lrt][c];rrt=son[rrt][c];
        }
    }return ret;
}
int main(){
	scanf("%d%d",&n,&m);
	int x;
	ins(las);
	for(int i=1;i<=n;i++){
        scanf("%d",&x);las^=x;ins(las);
	}
	char op[2];int l,r;
	while(m--){
        scanf("%s",op);
        if(op[0]=='A'){
            scanf("%d",&x);las^=x;ins(las);
        }
        else{
            scanf("%d%d%d",&l,&r,&x);
            printf("%d\n",ask(T[l-1],T[r],las^x));
        }
	}
	return 0;
}