主席树学习笔记

主席树学习笔记


说在前边:

  1. 之前了解过主席树的基础的思想,但是没有系统学习过,所以打算通过一些题目重新学习。

POJ2104

  1. 题意:静态区间查询 k-th number
  2. 思路:对每个位置开一颗权值线段树,维护前缀区间每个数字出现的次数,这样只需实现两棵线段树相减,利用简单的二分思想即可求出 k-th number。
  3. 问题:内存不足以开n颗线段树,时间自然也不够。
  4. 解决方法:注意到相邻两个位置,只会一个位置不同,那么发生改变的只有包含这个位置的一条链。那建树时,可以把新建这一条链,其他的边都接在上一个位置的线段树上即可。通过这样的方式我们保存了历史版本的线段树。
#include <iostream>
#include <cstdio>
#include <algorithm>
#define rep(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;
const int N = 1e5 + 100;
using namespace std;
int n, m;
struct node{ll v; int id;}a[N];
bool cmp(node a,node b){return a.v<b.v;}
int root[N], fid[N], tot;
struct tree{int l,r,num;}T[N*20];
void ins(int &x,int p,int l,int r) {T[++tot]=T[x]; x = tot; ++T[x].num;if(l == r) return;int mid = (l + r) >> 1;if(p <= mid) ins(T[x].l,p,l,mid);else ins(T[x].r,p,mid+1,r);
}
int ask(int pl,int pr,int k,int l,int r) {if(l==r)return l;int mid = (l+r)>>1;if(T[T[pr].l].num-T[T[pl].l].num>=k) return ask(T[pl].l,T[pr].l,k,l,mid);else return ask(T[pl].r, T[pr].r,k-(T[T[pr].l].num-T[T[pl].l].num),mid+1,r);
}
int main() {scanf("%d%d",&n,&m);rep(i,1,n)scanf("%lld",&a[i].v),a[i].id=i;sort(a+1,a+1+n,cmp);rep(i,1,n)fid[a[i].id]=i;rep(i,1,n)root[i]=root[i-1],ins(root[i],fid[i],1,n);rep(i,1,m) {int l,r,k;scanf("%d%d%d",&l,&r,&k);printf("%lld\n",a[ask(root[l-1],root[r],k,1,n)]);}return 0;
}

BZOJ1901

  1. 题意:动态区间查询 k-th number
  2. 思路:利用静态主席树思想维护权值线段树的前缀和,而本题要维护动态前缀和,那就可以想到利用树状数组,维护动态前缀和。
  3. 实现:
  • 实现单点修改操作:主席树的关键 - 保存历史版本,所以显然不能在已经建好的树上操作。需要新开一个节点作为这个位置新的根,在这个位置原来的位置上加一条新链。这个操作我们在实现静态前缀和的时候已经掌握了。前缀和时第i棵树是由第i-1棵树修改得到的,而修改操作中,新的这棵树是由它原先这个位置的值修改得到的。他们的本质都是修改操作。
  • 实现动态前缀和:T[i]表示树状数组中的下表为i代表的区间。利用主席树维护每个位置,前缀和利用树状数组来完成
//bzoj1901 ac
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define pb push_back
#define mem(W) memset(W,0,sizeof(W))
const int N = 6e4+5;
const int M = 1e4+5;
inline int read() {char c=getchar(); int x=0,f=1;while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f;
}
using namespace std;
int n,m,a[N];
vector<int> v;
int num;
int id(int x) {return lower_bound(v.begin(),v.end(),x)-v.begin()+1;}
struct node{int l,r,k;bool opt;} A[M];
struct tree{int l,r,num;}T[N*200];
int root[N],tot;
void ins(int &rt,int pre,int p,int l,int r,int d) {rt=++tot;T[rt]=T[pre];T[rt].num+=d;if(l==r)return;int mid=(l+r)>>1;if(p<=mid) ins(T[rt].l,T[pre].l,p,l,mid,d);else ins(T[rt].r,T[pre].r,p,mid+1,r,d);
}
void update(int p,int v) {for(int i=p;i<=n;i+=(i&-i)) ins(root[i],root[i],id(a[p]),1,num,-1);for(int i=p;i<=n;i+=(i&-i)) ins(root[i],root[i],id(v),1,num,1);a[p]=v;
}
int L[N],R[N],cntl,cntr;
int qury(int l,int r,int k) {if(l==r)return l;int mid=(l+r)>>1,sum=0;rep(i,1,cntr) sum+=T[T[R[i]].l].num;rep(i,1,cntl) sum-=T[T[L[i]].l].num;if(k<=sum) {rep(i,1,cntl) L[i]=T[L[i]].l;rep(i,1,cntr) R[i]=T[R[i]].l;return qury(l,mid,k);}else {rep(i,1,cntl) L[i]=T[L[i]].r;rep(i,1,cntr) R[i]=T[R[i]].r;return qury(mid+1,r,k-sum);}
}
int main() {int K;scanf("%d",&K);while(K--) {char s[5];scanf("%d%d",&n,&m); v.clear(); tot=0; mem(root);mem(T);rep(i,1,n) scanf("%d",a+i), v.pb(a[i]);rep(i,1,m) {scanf(" %s",s);if(s[0]=='Q')A[i].opt=1,scanf("%d%d%d",&A[i].l,&A[i].r,&A[i].k);else A[i].opt=0,scanf("%d%d",&A[i].l,&A[i].r),v.pb(A[i].r);}sort(v.begin(),v.end());v.erase(unique(v.begin(),v.end()),v.end());num = v.size();rep(i,1,n) for(int j=i;j<=n;j+=(j&(-j))) ins(root[j],root[j],id(a[i]),1,num,1);rep(ti,1,m) {if(A[ti].opt) {cntl=cntr=0;for(int i=A[ti].l-1;i;i-=(i&(-i))) L[++cntl]=root[i];for(int i=A[ti].r;i;i-=(i&(-i))) R[++cntr]=root[i];printf("%d\n",v[qury(1,num,A[ti].k)-1]);}elseupdate(A[ti].l,A[ti].r);}}return 0;
}

ZOJ2112

  1. 题意:与BZOJ1901相同,动态区间查询 k-th number,内存限制
  2. 思路:如果用上题的做法,内存无法接受。那么思考有什么地方可以减少节点的数量。注意到一开始读入的n个数,完全可以与后面的操作独立开,一颗静态的主席树,一颗用来修改,查询时将两棵树的信息合并即可。这样这n棵数的空间复杂度少了一个树状数组的log
//bzoj1901 ac
//zoj2112 ac
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define pb push_back
#define mem(W) memset(W,0,sizeof(W))
const int N = 6e4+5;
const int M = 1e4+5;
inline int read() {char c=getchar(); int x=0,f=1;while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f;
}
using namespace std;
int n,m,a[N];
vector<int> v;
int num;
int id(int x) {return lower_bound(v.begin(),v.end(),x)-v.begin()+1;}
struct node{int l,r,k;bool opt;} A[M];
struct tree{int l,r,num;}T[N*32];
int root[N],root0[N],tot;
void ins(int &rt,int pre,int p,int l,int r,int d) {rt=++tot;T[rt]=T[pre];T[rt].num+=d;if(l==r)return;int mid=(l+r)>>1;if(p<=mid) ins(T[rt].l,T[pre].l,p,l,mid,d);else ins(T[rt].r,T[pre].r,p,mid+1,r,d);
}
void update(int p,int v) {for(int i=p;i<=n;i+=(i&-i)) ins(root[i],root[i],id(a[p]),1,num,-1);for(int i=p;i<=n;i+=(i&-i)) ins(root[i],root[i],id(v),1,num,1);a[p]=v;
}
int L[N],R[N],cntl,cntr;
int qury(int l,int r,int ls,int rs,int k) {if(l==r)return l;int mid=(l+r)>>1,sum=0;rep(i,1,cntr) sum+=T[T[R[i]].l].num;rep(i,1,cntl) sum-=T[T[L[i]].l].num;sum += (T[T[rs].l].num-T[T[ls].l].num);if(k<=sum) {rep(i,1,cntl) L[i]=T[L[i]].l;rep(i,1,cntr) R[i]=T[R[i]].l;return qury(l,mid,T[ls].l,T[rs].l,k);}else {rep(i,1,cntl) L[i]=T[L[i]].r;rep(i,1,cntr) R[i]=T[R[i]].r;return qury(mid+1,r,T[ls].r,T[rs].r,k-sum);}
}
int main() {int K;scanf("%d",&K);while(K--) {char s[5];scanf("%d%d",&n,&m); v.clear(); tot=0; mem(root);mem(T);rep(i,1,n) scanf("%d",a+i), v.pb(a[i]);rep(i,1,m) {scanf(" %s",s);if(s[0]=='Q')A[i].opt=1,scanf("%d%d%d",&A[i].l,&A[i].r,&A[i].k);else A[i].opt=0,scanf("%d%d",&A[i].l,&A[i].r),v.pb(A[i].r);}sort(v.begin(),v.end());v.erase(unique(v.begin(),v.end()),v.end());num = v.size();rep(i,1,n) root0[i]=root0[i-1],ins(root0[i],root0[i],id(a[i]),1,num,1);rep(ti,1,m) {if(A[ti].opt) {cntl=cntr=0;for(int i=A[ti].l-1;i;i-=(i&(-i))) L[++cntl]=root[i];for(int i=A[ti].r;i;i-=(i&(-i))) R[++cntr]=root[i];printf("%d\n",v[qury(1,num,root0[A[ti].l-1],root0[A[ti].r],A[ti].k)-1]);}elseupdate(A[ti].l,A[ti].r);}}return 0;
}

洛谷P3567

  1. 题意:静态区间查询出现大于(r-l+1)/2次的数。
  2. 思路:出现超过(r-l+1)/2次的数至多有一个,所以直接在静态主席树上查询,区间内大于(r-l+1)/2的位置即可,与静态区间第k大的区别就是递归到右边时,不修改k值
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define pb push_back
typedef long long ll;
const int N = 500000+100;
using namespace std;
int n,m,a[N];
struct chair_tree{int l,r,num;}T[N*30];
int root[N],tot;
void ins(int &rt,int pre,int p,int d,int l,int r) {rt=++tot;T[rt]=T[pre];T[rt].num+=d;if(l==r)return;int mid = (l+r)>>1;if(p<=mid) ins(T[rt].l,T[pre].l,p,d,l,mid);else ins(T[rt].r,T[pre].r,p,d,mid+1,r);
}
int ask(int rtl,int rtr,int l,int r,int X) {if(l==r) return l;int mid = (l+r)>>1;if((T[T[rtr].l].num-T[T[rtl].l].num)*2>X) return ask(T[rtl].l,T[rtr].l,l,mid,X);else if((T[T[rtr].r].num-T[T[rtl].r].num)*2>X) return ask(T[rtl].r,T[rtr].r,mid+1,r,X);else return 0;
}
int main() {scanf("%d%d",&n,&m);rep(i,1,n)scanf("%d",&a[i]);rep(i,1,n)ins(root[i],root[i-1],a[i],1,1,n);rep(i,1,m) {int l,r;scanf("%d%d",&l,&r);printf("%d\n",ask(root[l-1],root[r],1,n,r-l+1));}return 0;
}

BZOJ2588

  1. 题意:无修改树上查询路径上的第k大
  2. 思路:考虑如何树上主席树,对每个从根节点延伸出的链,建前缀的主席树。那么这条路径上就是:
    \(u的值 + v的值 - lca(u,v)的值 - fa[lca(u,v)]的值\), 其他与静态区间第k大一致。
  3. lca求法:倍增5260 ms,树剖3900 ms

倍增lca

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define pb push_back
typedef long long ll;
const int N = 1e5 + 100;
using namespace std;
int n,m,a[N];
vector<int> v;
int num;
int id(int x) {return lower_bound(v.begin(),v.end(),x)-v.begin()+1;}
struct edge{int e,nxt;}E[N<<1];
int h[N],cc;
void add(int u,int v) {++cc;E[cc].e=v;E[cc].nxt=h[u];h[u]=cc;
}
int fa[N][20],dep[N];
int lca(int u,int v) {if(dep[u]<dep[v])swap(u,v);for(int i=17;i>=0;--i)if(dep[fa[u][i]]>=dep[v])u=fa[u][i];if(u==v)return u;for(int i=17;i>=0;--i) {if(fa[v][i]!=fa[u][i])v=fa[v][i],u=fa[u][i];}return fa[v][0];
}
struct chair_tree{int l,r,num;}T[N*20];
int root[N], tot;
void ins(int &rt,int pre,int p,int d,int l,int r) {rt=++tot;T[rt]=T[pre];T[rt].num+=d;if(l==r)return;int mid=(l+r)>>1;if(p<=mid) ins(T[rt].l,T[pre].l,p,d,l,mid);else ins(T[rt].r,T[pre].r,p,d,mid+1,r);
}
int vis[N];
void build_T() {queue<int> q;q.push(0);vis[0]=1;dep[0]=0;while(!q.empty()) {int u=q.front();q.pop();for(int i=1;i<=17;i++) fa[u][i]=fa[fa[u][i-1]][i-1];for(int i=h[u];~i;i=E[i].nxt) {int v=E[i].e;if(!vis[v]) {vis[v]=1;fa[v][0]=u;dep[v]=dep[u]+1;ins(root[v],root[u],id(a[v]),1,1,num);q.push(v);}}}
}
int ask(int ur,int vr,int lcar,int lcafr,int k,int l,int r) {if(l==r) return l;int mid=(l+r)>>1,sum=0;sum = T[T[ur].l].num - T[T[lcar].l].num + T[T[vr].l].num - T[T[lcafr].l].num;if(sum>=k) return ask(T[ur].l,T[vr].l,T[lcar].l,T[lcafr].l,k,l,mid);else return ask(T[ur].r,T[vr].r,T[lcar].r,T[lcafr].r,k-sum,mid+1,r);
}
int main() {scanf("%d%d",&n,&m);rep(i,1,n) scanf("%d",&a[i]),v.pb(a[i]);sort(v.begin(),v.end()); v.erase(unique(v.begin(),v.end()),v.end());num = v.size();memset(h,-1,sizeof(h));rep(i,1,n-1) {int u,v;scanf("%d%d",&u,&v);add(u,v),add(v,u);}a[0]=0; add(0,1),add(1,0);build_T();int lastans=0;rep(ti,1,m) {int x,y,k;scanf("%d%d%d",&x,&y,&k);x^=lastans;lastans = v[ask(root[x],root[y],root[lca(x,y)],root[fa[lca(x,y)][0]],k,1,num)-1];printf("%d\n",lastans);}return 0;
}

树剖lca

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define pb push_back
typedef long long ll;
const int N = 1e5 + 100;
using namespace std;
int n,m,a[N];
vector<int> v;
int num;
int id(int x) {return lower_bound(v.begin(),v.end(),x)-v.begin()+1;}
struct chair_tree{int l,r,num;}T[N*50];
int root[N], tot;
void ins(int &rt,int pre,int p,int d,int l,int r) {rt=++tot;T[rt]=T[pre];T[rt].num+=d;if(l==r)return;int mid=(l+r)>>1;if(p<=mid) ins(T[rt].l,T[pre].l,p,d,l,mid);else ins(T[rt].r,T[pre].r,p,d,mid+1,r);
}struct edge{int e,nxt;}E[N<<1];
int h[N],cc;
void add(int u,int v) {++cc;E[cc].e=v;E[cc].nxt=h[u];h[u]=cc;
}
int dep[N],fa[N],son[N],sz[N],top[N];
void dfs1(int u,int pre,int d) {dep[u]=d;fa[u]=pre;sz[u]=1;for(int i=h[u];~i;i=E[i].nxt) {int v=E[i].e;if(v!=pre) {ins(root[v],root[u],id(a[v]),1,1,num);dfs1(v,u,d+1);sz[u]+=sz[v];if(son[u]==-1||sz[v]>sz[son[u]]) son[u]=v;}}
}
void dfs2(int u,int sp) {top[u]=sp;if(son[u]==-1)return;dfs2(son[u],sp);for(int i=h[u];~i;i=E[i].nxt) {int v=E[i].e;if(v!=fa[u]&&v!=son[u])dfs2(v,v);}
}
void init() {memset(son,-1,sizeof(son));dfs1(0,-1,1);dfs2(0,0);
}
int lca(int u,int v) {while(top[u]!=top[v]){if(dep[top[u]]<dep[top[v]])swap(u,v);u=fa[top[u]];}if(dep[u]>dep[v])swap(u,v);return u;
}
int ask(int ur,int vr,int lcar,int lcafr,int k,int l,int r) {if(l==r) return l;int mid=(l+r)>>1,sum=0;sum = T[T[ur].l].num - T[T[lcar].l].num + T[T[vr].l].num - T[T[lcafr].l].num;if(sum>=k) return ask(T[ur].l,T[vr].l,T[lcar].l,T[lcafr].l,k,l,mid);else return ask(T[ur].r,T[vr].r,T[lcar].r,T[lcafr].r,k-sum,mid+1,r);
}
int main() {scanf("%d%d",&n,&m);rep(i,1,n) scanf("%d",&a[i]),v.pb(a[i]);sort(v.begin(),v.end()); v.erase(unique(v.begin(),v.end()),v.end());num = v.size();memset(h,-1,sizeof(h));rep(i,1,n-1) {int u,v;scanf("%d%d",&u,&v);add(u,v),add(v,u);}a[0]=0; add(0,1),add(1,0);init();int lastans=0;rep(ti,1,m) {int x,y,k;scanf("%d%d%d",&x,&y,&k);x^=lastans;lastans = v[ask(root[x],root[y],root[lca(x,y)],root[fa[lca(x,y)]],k,1,num)-1];if(ti!=m)printf("%d\n",lastans);else printf("%d",lastans);}return 0;
}

洛谷P4175

  1. 题意:带修改树上查询路径上的第k大
  2. 思路1:在前一题的基础上,考虑如何修改。当一个节点更新之后受影响的只有它的子树,处理dfs序,对于节点i相当于要修改\([ L[i],R[i] ]\)这个区间的值,树状数组差分实现区间修改单点查询。
  3. 询问方法:看大佬Blog都是,直接将[1,L[i]]当作树上根到节点i的前缀,套用常规静态的方法。讲道理,感觉很奇怪。。。但还是过了
  4. 思路2:把修改改成单点修改,树剖的做法,也可以过,这个正确性很显然了。

做法1:

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define pb push_back
typedef long long ll;
inline int read() {char c=getchar();int x=0,f=1;while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f;
}
using namespace std;
const int N = 1e5 + 100;
struct Q{int a,b,k;}q[N];
struct edge{int e,nxt;}E[N<<1];
int h[N],cc;
void add(int u,int v) {++cc;E[cc].e=v;E[cc].nxt=h[u];h[u]=cc;}
int n,m,a[N];
vector<int> mp;
int num;
int id(int x){return lower_bound(mp.begin(),mp.end(),x)-mp.begin()+1;}struct chiarman{int l,r,num;}T[N*200];
int tot,rt[N],rtc[N]; 
void ins(int &rt,int pre,int p,int d,int l,int r) {rt=++tot;T[rt]=T[pre];T[rt].num+=d;if(l==r)return;int mid=(l+r)>>1;if(p<=mid) ins(T[rt].l,T[pre].l,p,d,l,mid);else ins(T[rt].r,T[pre].r,p,d,mid+1,r);
}int dep[N],fa[N],son[N],sz[N],top[N],tid[N],L[N],R[N],pos;
void dfs1(int u,int pre,int d) {dep[u]=d;fa[u]=pre;sz[u]=1;for(int i=h[u];~i;i=E[i].nxt) {int v=E[i].e;if(v!=pre) {dfs1(v,u,d+1);sz[u]+=sz[v];if(son[u]==-1||sz[v]>sz[son[u]]) son[u]=v;}}
}
void dfs2(int u,int sp) {top[u]=sp;L[u]=tid[u]=++pos;if(son[u]==-1)return;dfs2(son[u],sp);for(int i=h[u];~i;i=E[i].nxt) {int v=E[i].e;if(v!=fa[u]&&v!=son[u])dfs2(v,v);}R[u]=pos;
}
void init_tp() {memset(son,-1,sizeof(son));dfs1(1,0,1);dfs2(1,1);
}
int lca(int u,int v) {while(top[u]!=top[v]){if(dep[top[u]]<dep[top[v]])swap(u,v);u=fa[top[u]];}if(dep[u]>dep[v])swap(u,v);return u;
}void build(int u){if(u==-1) return;ins(rt[u],rt[fa[u]],id(a[u]),1,1,num);build(son[u]);for(int i=h[u];~i;i=E[i].nxt)if(E[i].e!=fa[u]&&E[i].e!=son[u]) build(E[i].e);
}void B_add(int x,int p,int d){for(int i=x;i<=pos;i+=(i&-i)) ins(rtc[i],rtc[i],p,d,1,num);
}vector<int> A,B;
int ck(int k) {int sum=0;rep(i,0,A.size()-1)sum+=T[A[i]].num;rep(i,0,B.size()-1)sum-=T[B[i]].num;return (sum>=k);
}
int qury(int l,int r,int k) {if(l==r)return l;int mid=(l+r)>>1,sum=0;rep(i,0,A.size()-1)sum+=T[T[A[i]].r].num;rep(i,0,B.size()-1)sum-=T[T[B[i]].r].num;if(sum>=k) {rep(i,0,A.size()-1)A[i]=T[A[i]].r;rep(i,0,B.size()-1)B[i]=T[B[i]].r;return qury(mid+1,r,k);}else {rep(i,0,A.size()-1)A[i]=T[A[i]].l;rep(i,0,B.size()-1)B[i]=T[B[i]].l;return qury(l,mid,k-sum);}
}
int cal() {int sum=0;rep(i,0,A.size()-1)sum+=T[T[A[i]].r].num;rep(i,0,B.size()-1)sum-=T[T[B[i]].r].num;return sum;
}
void chai(int u,int v) {while(top[u]!=top[v]) {if(dep[top[u]]<dep[top[v]]) swap(u,v);for(int i=tid[u];i;i-=(i&-i))A.pb(rtc[i]);for(int i=tid[top[u]]-1;i;i-=(i&-i))B.pb(rtc[i]);u=fa[top[u]];}if(dep[u]>dep[v])swap(u,v);for(int i=tid[v];i;i-=(i&-i))A.pb(rtc[i]);for(int i=tid[u]-1;i;i-=(i&-i))B.pb(rtc[i]);return ;
}void ask(int u,int v,int k) {int p=lca(u,v);A.clear();B.clear();A.pb(rt[u]),A.pb(rt[v]),B.pb(rt[p]),B.pb(rt[fa[p]]);chai(u,v);if(!ck(k)) {puts("invalid request!");return;}printf("%d\n",mp[qury(1,num,k)-1]);return;
}
int main() {scanf("%d%d",&n,&m);memset(h,-1,sizeof(h));memset(son,-1,sizeof(son));rep(i,1,n) a[i] = read(), mp.pb(a[i]);rep(i,1,n-1) {int u=read(),v=read();add(u,v),add(v,u);}rep(i,1,m) {q[i].k=read(),q[i].a=read(),q[i].b=read();if(q[i].k==0)mp.pb(q[i].b);}sort(mp.begin(),mp.end()); mp.erase(unique(mp.begin(),mp.end()),mp.end());num = mp.size();init_tp();build(1);rep(i,1,m) {if(q[i].k==0) {B_add(tid[q[i].a],id(a[q[i].a]),-1);a[q[i].a]=q[i].b;B_add(tid[q[i].a],id(a[q[i].a]),1);}else {ask(q[i].a,q[i].b,q[i].k);}}return 0;
}

做法2:

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define pb push_back
typedef long long ll;
inline int read() {char c=getchar();int x=0,f=1;while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f;
}
using namespace std;
const int N = 1e5 + 100;
struct Q{int a,b,k;}q[N];
struct edge{int e,nxt;}E[N<<1];
int h[N],cc;
void add(int u,int v) {++cc;E[cc].e=v;E[cc].nxt=h[u];h[u]=cc;}
int n,m,a[N];
vector<int> mp;
int num;
int id(int x){return lower_bound(mp.begin(),mp.end(),x)-mp.begin()+1;}struct chiarman{int l,r,num;}T[N*200];
int tot,rt[N],rtc[N]; 
void ins(int &rt,int pre,int p,int d,int l,int r) {rt=++tot;T[rt]=T[pre];T[rt].num+=d;if(l==r)return;int mid=(l+r)>>1;if(p<=mid) ins(T[rt].l,T[pre].l,p,d,l,mid);else ins(T[rt].r,T[pre].r,p,d,mid+1,r);
}int dep[N],fa[N],son[N],sz[N],top[N],tid[N],L[N],R[N],pos;
void dfs1(int u,int pre,int d) {dep[u]=d;fa[u]=pre;sz[u]=1;for(int i=h[u];~i;i=E[i].nxt) {int v=E[i].e;if(v!=pre) {dfs1(v,u,d+1);sz[u]+=sz[v];if(son[u]==-1||sz[v]>sz[son[u]]) son[u]=v;}}
}
void dfs2(int u,int sp) {top[u]=sp;L[u]=tid[u]=++pos;if(son[u]==-1)return;dfs2(son[u],sp);for(int i=h[u];~i;i=E[i].nxt) {int v=E[i].e;if(v!=fa[u]&&v!=son[u])dfs2(v,v);}R[u]=pos;
}
void init_tp() {memset(son,-1,sizeof(son));dfs1(1,0,1);dfs2(1,1);
}
int lca(int u,int v) {while(top[u]!=top[v]){if(dep[top[u]]<dep[top[v]])swap(u,v);u=fa[top[u]];}if(dep[u]>dep[v])swap(u,v);return u;
}void build(int u){if(u==-1) return;ins(rt[u],rt[fa[u]],id(a[u]),1,1,num);build(son[u]);for(int i=h[u];~i;i=E[i].nxt)if(E[i].e!=fa[u]&&E[i].e!=son[u]) build(E[i].e);
}void B_add(int x,int p,int d){for(int i=x;i<=pos;i+=(i&-i)) ins(rtc[i],rtc[i],p,d,1,num);
}vector<int> A,B;
int ck(int k) {int sum=0;rep(i,0,A.size()-1)sum+=T[A[i]].num;rep(i,0,B.size()-1)sum-=T[B[i]].num;return (sum>=k);
}
int qury(int l,int r,int k) {if(l==r)return l;int mid=(l+r)>>1,sum=0;rep(i,0,A.size()-1)sum+=T[T[A[i]].r].num;rep(i,0,B.size()-1)sum-=T[T[B[i]].r].num;if(sum>=k) {rep(i,0,A.size()-1)A[i]=T[A[i]].r;rep(i,0,B.size()-1)B[i]=T[B[i]].r;return qury(mid+1,r,k);}else {rep(i,0,A.size()-1)A[i]=T[A[i]].l;rep(i,0,B.size()-1)B[i]=T[B[i]].l;return qury(l,mid,k-sum);}
}
int cal() {int sum=0;rep(i,0,A.size()-1)sum+=T[T[A[i]].r].num;rep(i,0,B.size()-1)sum-=T[T[B[i]].r].num;return sum;
}
void chai(int u,int v) {while(top[u]!=top[v]) {if(dep[top[u]]<dep[top[v]]) swap(u,v);for(int i=tid[u];i;i-=(i&-i))A.pb(rtc[i]);for(int i=tid[top[u]]-1;i;i-=(i&-i))B.pb(rtc[i]);u=fa[top[u]];}if(dep[u]>dep[v])swap(u,v);for(int i=tid[v];i;i-=(i&-i))A.pb(rtc[i]);for(int i=tid[u]-1;i;i-=(i&-i))B.pb(rtc[i]);return ;
}void ask(int u,int v,int k) {int p=lca(u,v);A.clear();B.clear();A.pb(rt[u]),A.pb(rt[v]),B.pb(rt[p]),B.pb(rt[fa[p]]);chai(u,v);if(!ck(k)) {puts("invalid request!");return;}printf("%d\n",mp[qury(1,num,k)-1]);return;
}
int main() {scanf("%d%d",&n,&m);memset(h,-1,sizeof(h));memset(son,-1,sizeof(son));rep(i,1,n) a[i] = read(), mp.pb(a[i]);rep(i,1,n-1) {int u=read(),v=read();add(u,v),add(v,u);}rep(i,1,m) {q[i].k=read(),q[i].a=read(),q[i].b=read();if(q[i].k==0)mp.pb(q[i].b);}sort(mp.begin(),mp.end()); mp.erase(unique(mp.begin(),mp.end()),mp.end());num = mp.size();init_tp();build(1);rep(i,1,m) {if(q[i].k==0) {B_add(tid[q[i].a],id(a[q[i].a]),-1);a[q[i].a]=q[i].b;B_add(tid[q[i].a],id(a[q[i].a]),1);}else {ask(q[i].a,q[i].b,q[i].k);}}return 0;
}

BZOJ2212

  1. 题意:现在有一棵二叉树,所有非叶子节点都有两个孩子。在每个叶子节点上有一个权值(有n个叶子节点,满足这些权值为1..n的一个排列)。可以任意交换每个非叶子节点的左右孩子。要求进行一系列交换,使得最终所有叶子节点的权值按照遍历序写出来,逆序对个数最少。
  2. 思路:动态开点,给每个叶子节点开一颗权值线段树,自底向上合并线段树,合并的过程中通过左右子树的权值线段树计算逆序数。
  3. 线段树合并及复杂度分析:大佬的Blog
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;
const int N = 8000010;
using namespace std;
int n,rt;
struct tr{int l,r,num;}tree[N],T[N];
int cc,tot,root[N];
void build(int &rt) {rt = ++cc;int x;scanf("%d",&x);if(x) tree[rt].num=x;else build(tree[rt].l),build(tree[rt].r);
}
void ins(int &rt,int pre,int p,int d,int l,int r){rt=++tot;T[rt]=T[pre];T[rt].num+=d;if(l==r){T[rt].num=1;return;}int mid=(l+r)>>1;if(p<=mid) ins(T[rt].l,T[pre].l,p,d,l,mid);else ins(T[rt].r,T[pre].r,p,d,mid+1,r);T[rt].num = T[T[rt].l].num + T[T[rt].r].num;
}
ll ans1,ans2,ans;
int merge(int x,int y) {if(!x)return y;if(!y)return x;ans1 += 1LL*T[T[x].l].num*T[T[y].r].num;ans2 += 1LL*T[T[x].r].num*T[T[y].l].num;T[x].l = merge(T[x].l,T[y].l);T[x].r = merge(T[x].r,T[y].r);T[x].num = T[T[x].l].num + T[T[x].r].num;return x;
}
void solve(int rt) {if(tree[rt].num) return;solve(tree[rt].l),solve(tree[rt].r);ans1=ans2=0;root[rt] = merge(root[tree[rt].l],root[tree[rt].r]);ans+=min(ans1,ans2);
}
int main() {scanf("%d",&n);build(rt);rep(i,1,cc)if(tree[i].num)ins(root[i],root[i],tree[i].num,1,1,n);solve(rt);printf("%lld\n",ans);return 0;
}

这个专题就先到这了。。。

转载于:https://www.cnblogs.com/RRRR-wys/p/9211634.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/320758.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

初一模拟赛总结(2019.5.25)

成绩&#xff1a; 本蒟蒻竟AKAKAK了&#xff0c;不敢相信 &#xff89;)&#xff9f;Д&#xff9f;( rankrankranknamenamenamescorescorescoreT1T1T1T2T2T2T3T3T3T4T4T4111lyflyflyf400400400100100100100100100100100100100100100222hkyhkyhky3203203201001001001001001006…

.NET Core开发日志——HttpClientFactory

当需要向某特定URL地址发送HTTP请求并得到相应响应时&#xff0c;通常会用到HttpClient类。该类包含了众多有用的方法&#xff0c;可以满足绝大多数的需求。但是如果对其使用不当时&#xff0c;可能会出现意想不到的事情。博客园官方团队就遇上过这样的问题&#xff0c;国外博主…

YbtOJ#20067-[NOIP2020模拟赛B组Day5]糖果分配【dp】

正题 题目链接:http://noip.ybtoj.com.cn/contest/102/problem/1 题目大意 nnn个xix_ixi​在[li,ri][l_i,r_i][li​,ri​]中随机选择&#xff0c;给出一个ccc&#xff0c;一个序列∑kic\sum k_ic∑ki​c 每种方案贡献为∏i1nxiki\prod_{i1}^nx_i^{k_i}i1∏n​xiki​​ 解题思路…

Wannafly挑战赛18

Wannafly挑战赛18 A. 序列 先考虑暴力&#xff0c;相邻两个树之间乘上给定的三种数&#xff0c;递推出下一个位置填什么&#xff0c;然后再check一下&#xff0c;最后一位是否为1即可。这样时间显然不行&#xff0c;但是给我们一种思路&#xff0c;就是中间的转换关系&#xff…

【拓扑排序】【DP】旅行计划(luogu 1137)

旅行计划 luogu 1137 题目大意&#xff1a; 有一堆点&#xff0c;之间连接着一些边&#xff08;有向&#xff09;&#xff0c;保证无环&#xff0c;现在要求出从任意地方出发到所有点的最长路&#xff08;出发点不一定相同&#xff09; 原题&#xff1a; 题目描述 小明要…

【asp.net Core MVC + angular6实战】 - 1. 环境搭建

为什么打算写这些文章&#xff1f;没有为什么&#xff0c;只是为了学习Angular和更了解.Net Core等技术需要用到的技术&#xff1f;后端使用.Net Core 2.1 EF Core 2.1 Mysql 5.7 Identity &#xff08;不知道Identity算不算一个独立的技术点&#xff09;前端主要使用的是An…

YbtOJ#20070-[NOIP2020模拟赛B组Day5]诗人小K【状压dp】

正题 题目链接:http://noip.ybtoj.com.cn/contest/102/problem/4 题目大意 求有多少个长度为nnn的序列aaa满足1≤ai≤101\leq a_i\leq 101≤ai​≤10&#xff0c;且可以找到一组(i,j,k,l)(i,j,k,l)(i,j,k,l)使得(∑pij−1apx)&(∑pjk−1apy)&(∑pklapz)(\sum_{pi}^{j…

Codeforces Round #491 (Div. 2)

Codeforces Round #491 (Div. 2) A. If at first you dont succeed... 按题意判断 #include <bits/stdc.h> #define rep(i,a,b) for(int ia;i<b;i) #define pb push_back #define mem(W) memset(W,0,sizeof(W)) typedef long long ll; inline int read() {char cgetch…

学习分享会(2019.5.31)

学习分享会 First&#xff08;初三大佬发言&#xff09; 初三大佬在为初二大佬讲一堆哲学的道理&#xff0c;虽然十分高深&#xff0c;但还是听懂了一点点 Second&#xff08;初二大佬发言&#xff09; 初二大佬开始“秀”&#xff0c;首先是大佬LW讲了讲十分常识的东西&am…

微软宣布公开预览Dev Spaces for AKS

微软宣布公开预览面向Azure Kubernetes Services&#xff08;AKS&#xff09;的Dev Spaces&#xff0c;为团队提供了一种快速的Kubernetes迭代开发体验。该版本是继5月份Build 2018大会上Dev Spaces内部预览之后的版本。借助这个版本&#xff0c;微软希望为开发人员提供一种在A…

YbtOJ#20068-[NOIP2020模拟赛B组Day5]连通子图【构造】

正题 题目链接:http://noip.ybtoj.com.cn/contest/102/problem/2 题目大意 求构造一个包含根节点的联通子图kkk个的树。 解题思路 现在考虑一棵树&#xff0c;如果我们在根节点处加一个点&#xff0c;那么方案数会2\times 22。如果在根节点上加入一个父节点&#xff08;根会…

2018 ACM-ICPC World Finals - Beijing

2018 ACM-ICPC World Finals - Beijing A. Catch the Plane \(dp[v_i,t_i]\)表示时刻\(t_i\)在\(v_i\)点&#xff0c;到达终点的最大概率&#xff0c;那么转移方程为&#xff1a;\(dp[(v_i,t_i)] max(P_{ij}*dp[(v_{j1},t_{j1})] (1-Pij)*dp[(v_{i1},t_{i1})])\)\(dp[(v_i,t_…

解析Visual C# 7.2中的private protected访问修饰符

去年12月份&#xff0c;随着Visual Studio 2017 Update 15.5的发布&#xff0c;Visual C#迎来了它的最新版本&#xff1a;7.2. 在这个版本中&#xff0c;有个让人难以理解的新特性&#xff0c;就是private protected访问修饰符&#xff08;Access Modifier&#xff09;。至此&a…

【Floyed】廉价最短路径

廉价最短路径 题目大意&#xff1a; 一个图中&#xff0c;在满足最短路的前提下&#xff0c;求最小代价 原题&#xff1a; 题目描述 图是由一组顶点和一组边组成的。一条边连接两个顶点。例如&#xff0c;图1表示了一个有4个顶点V、5条边的图。图中&#xff0c;每条边e是有…

CF401C-Team【构造】

正题 题目链接:https://www.luogu.com.cn/problem/CF401C 题目大意 构造一个序列包含nnn个000和mmm个111且不含有连续两个000和连续三个111。 解题思路 考虑用110110110和101010来构造序列即可。 codecodecode #include<cstdio> #include<cstring> #include<…

哈希学习笔记

hash学习笔记 常用函数: $ hash[i] \sum _{ji} ^{len-1} {s[j]*X^{j-i}}, X ≥ |字符集| $ 取多个模&#xff0c;对于一个子串\(s[i]s[i1]..s[j]\)的\(hash hash[i] - hash[j1]*X^{j-i1}\)&#xff0c;预处理\(hash[i]\)以及\(X^i\)即可\(O(1)\)求出所有的子串hash值一般运用…

Net Core集成Exceptionless分布式日志功能以及全局异常过滤

相信很多朋友都看过我的上篇关于Exceptionless的简单入门教程[asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程][https://www.cnblogs.com/yilezhu/p/9193723.html] 上篇文章只是简单的介绍了Exceptionless是什么&#xff1f;能做什么呢…

【斐波那契】【前缀和】无限序列

无限序列 题目大意&#xff1a; 有这样一个规则&#xff1a;1.把“1”变成“10” 2.把“0”变成“1” 一个序列的第一位是“1” 然后是&#xff1a;“10”&#xff0c;“101”…… 序列无限次操作后会得到“1011010110110101101……” 问某一个区间内有多少个“1” 原题&…

CF496E-Distributing Parts【平衡树,贪心】

正题 题目链接:https://www.luogu.com.cn/problem/CF496E 题目大意 nnn个[li,ri][l_i,r_i][li​,ri​]&#xff0c;mmm个[ai,bi,ki][a_i,b_i,k_i][ai​,bi​,ki​]表示可以覆盖掉kik_iki​个ai≤li≤ri≤bia_i\leq l_i\leq r_i\leq b_iai​≤li​≤ri​≤bi​的区间&#xff0…

kmp学习笔记

kmp&#xff0c;扩展 kmp 学习笔记 说再前边 字符串水平基本为0&#xff0c;学了第4遍KMP了&#xff0c;总是忘。。。网上资料很多&#xff0c;就不详细讲解了。抄的kuangbin神犇模板 kmp 一些知识 循环节大小&#xff1a;n - nxt[n]模板[HDU2087] 下标从0开始 nxt[i] 为满足p[…