主席树入门
刷了那么多线段树的题终于可以搞一下可持久化线段树了。
首先是 放一道最最最简单的例题:
对于这个需要访问历史的线段树来说 我想应该是一棵可持久化线段树的东西。
对于一棵 线段树我们想让其可持久化 那么应该是 让其点接着以前的点接(如果没有修改的话)
我想修改的话就必须搞一波树套树了(尽管我不会但是 我还是不会HH)
其实把线段树学通了,那么这个 主席树当然也算是很简单的了。
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<iomanip> #include<cstring> #include<string> #include<cstdlib> #include<cmath> #include<algorithm> #include<cctype> #include<utility> #include<set> #include<bitset> #include<queue> #include<stack> #include<deque> #include<map> #include<vector> #include<ctime> #define INF 2147483646 #define h(p) t[p].h #define l(p) t[p].x #define k(p) t[p].k using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void put(int x) { x<0?putchar('-'),x=-x:0; int num=0;char ch[50]; while(x)ch[++num]=x%10+'0',x/=10; num==0?putchar('0'):0; while(num)putchar(ch[num--]); putchar('\n');return; } const int MAXN=1000002; struct wy { int lc,rc; int v; }t[MAXN<<4]; int tot,root[MAXN]; int n,m,cnt; int a[MAXN],ans; int build(int l,int r) { int p=++tot; if(l==r){t[p].v=a[l];return p;} int mid=(l+r)>>1; t[p].lc=build(l,mid); t[p].rc=build(mid+1,r); return p; } int insert(int now,int l,int r,int x,int d) { int p=++tot; t[p]=t[now]; if(l==r) { t[p].v=d; return p; } int mid=(l+r)>>1; if(x<=mid)t[p].lc=insert(t[p].lc,l,mid,x,d); else t[p].rc=insert(t[p].rc,mid+1,r,x,d); return p; } void ask(int p,int l,int r,int x) { if(l==r){ans=t[p].v;return;} int mid=(l+r)>>1; if(x<=mid)ask(t[p].lc,l,mid,x); else ask(t[p].rc,mid+1,r,x); } int main() { //freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=n;i++)a[i]=read(); root[0]=build(1,n); for(int i=1;i<=m;i++) { int p,mark,x,d; p=read();mark=read(); if(mark==1) { x=read();d=read(); root[++cnt]=insert(root[p],1,n,x,d); } if(mark==2) { x=read(); root[++cnt]=++tot; t[tot]=t[root[p]]; ask(root[p],1,n,x); put(ans); } } //cout<<endl; //for(int i=0;i<=cnt;i++)put(root[i]); return 0; }
这道题 呢 很显然 (题目告诉我们是一棵主席树且不带区间修改求第k小)
那么我们 呢 可以搞一波事情 。
就是 线段树直接的相加减可以得到区间的信息 ,当然线段树要建一棵权值线段树。
千万不要像我一样直接码 因为码到最后一行输出意识到自己没用线段树相加减 (自己写的当时也没有这个想法)
然后就是感觉对的,然后到最后一行意识到自己建的主席树完全错了。
于是全删了愚蠢的思路我就不再提了这里我们要建 nlogn棵线段树即可解决问题!
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<iomanip> #include<cstring> #include<string> #include<cstdlib> #include<cmath> #include<algorithm> #include<cctype> #include<utility> #include<set> #include<bitset> #include<queue> #include<stack> #include<deque> #include<map> #include<vector> #include<ctime> #define INF 2147483646 #define h(p) t[p].h #define l(p) t[p].x #define k(p) t[p].k using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void put(int x) { x<0?putchar('-'),x=-x:0; int num=0;char ch[50]; while(x)ch[++num]=x%10+'0',x/=10; num==0?putchar('0'):0; while(num)putchar(ch[num--]); putchar('\n');return; } const int MAXN=100002,maxn=5001; struct wy { int lc,rc; int v; }t[MAXN<<5];//其实准确(较粗略)的空间复杂度为 4*n+nlogn==(4+logn)*n logn=6/0,3=20 24*n int n,m,q; int a[MAXN],b[MAXN]; int root[MAXN],cnt,tot,ans; void discrete() { sort(b+1,b+1+n); for(int i=1;i<=n;i++)if(i==1||b[i]!=b[i-1])b[++m]=b[i]; for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+1+m,a[i])-b; } int build(int l,int r) { int p=++tot; if(l==r)return p; int mid=(l+r)>>1; build(l,mid); build(mid+1,r); return p; } void ask(int x,int y,int l,int r,int k) { if(l==r){ans=l;return;} int c=t[t[y].lc].v-t[t[x].lc].v; int mid=(l+r)>>1; if(k>c)ask(t[x].rc,t[y].rc,mid+1,r,k-c); else ask(t[x].lc,t[y].lc,l,mid,k); return; } int insert(int now,int l,int r,int d) { int p=++tot; t[p]=t[now]; if(l==r){t[p].v=t[now].v+1;return p;} int mid=(l+r)>>1; if(d<=mid)t[p].lc=insert(t[p].lc,l,mid,d); else t[p].rc=insert(t[p].rc,mid+1,r,d); t[p].v=t[t[p].lc].v+t[t[p].rc].v; return p; } int main() { //freopen("1.in","r",stdin); n=read();q=read(); for(int i=1;i<=n;i++)a[i]=b[i]=read(); discrete(); //for(int i=1;i<=n;i++)cout<<a[i]<<' '<<b[a[i]]<<endl; root[0]=build(1,n); for(int i=1;i<=n;i++)root[i]=insert(root[i-1],1,n,a[i]); for(int i=1;i<=q;i++) { int x,y,k; x=read();y=read();k=read(); ask(root[x-1],root[y],1,n,k); put(b[ans]); } return 0; }
那么这道题呢 比较难想吧因为 要维护的东西很多看 n,m的范围然后 很迷啊,怎么求呢。
首先考虑两点之间的点权 怎么搞 肯定是LCA 的题!
但是没有那么简单 求两点 之间的点权第k小的 那么 尧神是小小的点播了一番我
先做这样的一个题目 求 任意两点之间的点权之和
被嘲讽了,然后想出 对于一个节点每个维护一到根节点之间的点权之和即可在求出LCA的情况之下
O(1)的时间内求出一些东西!
有两种解决方法 d[x]+d[y]-2*d[LCA]+v[LCA] d[x]+d[y]-d[LCA]-d[fLCA]
都是可以的 然后就是 一波主席树 没了
值得一提的是第一种方法有坑点 我犯了一个低级错误23333
于是有了如下代码:
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<iomanip> #include<cstring> #include<string> #include<cstdlib> #include<cmath> #include<algorithm> #include<cctype> #include<utility> #include<set> #include<bitset> #include<queue> #include<stack> #include<deque> #include<map> #include<vector> #include<ctime> using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void put(int x) { x<0?putchar('-'),x=-x:0; int num=0;char ch[50]; while(x)ch[++num]=x%10+'0',x/=10; num==0?putchar('0'):0; while(num)putchar(ch[num--]); return; } const int MAXN=100002; int n,m,T,s=1,q[MAXN],w; int a[MAXN],b[MAXN]; int depth[MAXN],f[MAXN][25];//f[i][j]表示i节点向上爬2^j个单位的祖先 int lin[MAXN<<1],nex[MAXN<<1],ver[MAXN<<1],len=0; int root[MAXN],cnt,tot,ans; struct wy { int lc,rc; int v; }t[MAXN<<5]; void discrete() { sort(b+1,b+1+n); for(int i=1;i<=n;i++)if(i==1||b[i]!=b[i-1])b[++m]=b[i]; for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+1+m,a[i])-b; } void add(int x,int y) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; } void bfs() { int h=0,t=0; q[++t]=s; depth[s]=1; while(h++<t) { int te=q[h]; for(int i=lin[te];i;i=nex[i]) { int tn=ver[i]; if(depth[tn]!=0)continue; depth[tn]=depth[te]+1; f[tn][0]=te; for(int j=1;j<=w;j++)f[tn][j]=f[f[tn][j-1]][j-1]; q[++t]=tn; } } } int LCA(int x,int y) { if(depth[x]>depth[y]){int t;t=x;x=y;y=t;} if(depth[x]!=depth[y]) for(int i=w;i>=0;i--) if(depth[f[y][i]]>=depth[x])y=f[y][i]; if(x==y)return x; for(int i=w;i>=0;i--) if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; return f[x][0]; } int build(int l,int r) { int p=++tot; if(l==r)return p; int mid=(l+r)>>1; t[p].lc=build(l,mid); t[p].rc=build(mid+1,r); return p; } int insert(int now,int l,int r,int d) { int p=++tot; t[p]=t[now]; if(l==r){t[p].v=t[now].v+1;return p;} int mid=(l+r)>>1; if(d<=mid)t[p].lc=insert(t[p].lc,l,mid,d); else t[p].rc=insert(t[p].rc,mid+1,r,d); t[p].v=t[t[p].lc].v+t[t[p].rc].v; return p; } void dfs(int x,int father) { for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(tn==father)continue; root[tn]=insert(root[x],1,m,a[tn]); dfs(tn,x); } } void ask(int lca,int x,int y,int l,int r,int d,int k)//公式显然为 t[x]+t[y]-2*t[lca]+(l<=d<=mid)?1:0; { if(l==r){ans=l;return;} int c=0; int mid=(l+r)>>1; if(d<=mid&&d>=l)c++;//注意!!! c+=t[t[x].lc].v+t[t[y].lc].v-2*t[t[lca].lc].v; if(c<k)ask(t[lca].rc,t[x].rc,t[y].rc,mid+1,r,d,k-c); else ask(t[lca].lc,t[x].lc,t[y].lc,l,mid,d,k); return; } int main() { //freopen("1.in","r",stdin); n=read();T=read(); for(int i=1;i<=n;i++)a[i]=b[i]=read(); discrete(); for(int i=1;i<n;i++) { int x,y; x=read();y=read(); add(x,y);add(y,x); } w=(int)(log(n*1.0)/log(2*1.0))+1; bfs(); root[cnt]=build(1,m); root[s]=insert(root[cnt],1,m,a[s]); dfs(s,0); //cout<<m<<endl; //cout<<b[m]<<endl; //for(int i=1;i<=n;i++)put(t[root[i]].v); //cout<<T<<endl; for(int i=1;i<=T;i++) { int x,y,k; x=read();y=read();k=read(); x=x^ans;int lca=LCA(x,y); //cout<<x<<' '<<y<<endl; //cout<<(x^ans)<<' '<<lca<<endl; ask(root[lca],root[x],root[y],1,m,a[lca],k); //cout<<ans<<endl; ans=b[ans]; put(ans); if(i!=T)puts(""); } return 0; }
效果当然还不错.