主席树全称是可持久化权值线段树,主要解决区间第k大问题
注意空间大小为NlogN,一般可以开到N<<5
1.P4587 [FJOI2016]神秘数
【题意】给定一个数列,求最小的不能被l-r集合中的数表示的值
【分析】这个题目有一个很好的思想,类似于货币?
如果当前已经表示出1-pos,集合中最大的为mx,那么新加入的一个值一定在[mx+1,pos+1]才对结果又影响,如果小于mx+1,那么和以前一样,如果大于pos+1,中间有断开的位置,所以答案还是不会变,所以我们的问题变成了求l-r中范围在mx+1到pos+1的值的和,用主席树处理即可
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
int n,m;
int a[maxn];
int root[maxn];
struct point
{
int sum,l,r;
}tr[maxn*105];
int cnt;
void update(int &now,int l,int r,int val)
{
tr[++cnt]=tr[now]; now=cnt; tr[cnt].sum+=val;
if(l==r) return;
int mid=l+r>>1;
if(val<=mid) update(tr[now].l,l,mid,val);
else update(tr[now].r,mid+1,r,val);
}
int query(int rx,int ry,int L,int R,int l,int r)
{
if(!(tr[ry].sum-tr[rx].sum)) return 0;
if(L>=l && R<=r) return tr[ry].sum-tr[rx].sum;
int mid=L+R>>1;
int res=0;
if(l<=mid) res+=query(tr[rx].l,tr[ry].l,L,mid,l,r);
if(mid<r) res+=query(tr[rx].r,tr[ry].r,mid+1,R,l,r);
return res;
}
int main()
{
freopen("mystery.in","r",stdin);
freopen("mystery.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),root[i]=root[i-1],update(root[i],1,inf,a[i]);
scanf("%d",&m);
int x,y;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
int mx=0,pos=0;
for(int sum;;)
{
sum=query(root[x-1],root[y],1,inf,mx+1,pos+1);
if(!sum) break;
mx=pos+1; pos+=sum;
}
printf("%d\n",pos+1);
}
return 0;
}
2.P3293 [SCOI2016]美味
【题意】给定一个数列a,给定一个l-r,对于每个b,x,l,r,求b xor (x+a[i]) (l<=i<=r)的最大值
【分析】这个异或的操作,明显让我们想到了从高位到低位枚举的贪心思想
对于每一位,如果b的第i位为0,那么最好的情况为第i位为1,i位之前的已经确定为ans,那么新的数的范围为ans-x到ans-x-(1<<k)-1,如果在这个范围内有值,那就可以使得第i位为1,
b的第i为1也是同理
在l-r求值域范围的和,用主席树维护即可
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+5;
int n,m,a[maxn];
int root[maxn];
struct segtree
{
int l,r,sum;
}tr[maxn<<5];
int cnt;
void update(int &rt,int last,int L,int R,int val)
{
if(val<L || val>R) return;
rt=++cnt; tr[rt]=tr[last]; tr[rt].sum++;
if(L==R) return;
int mid=L+R>>1;
update(tr[rt].l,tr[last].l,L,mid,val);
update(tr[rt].r,tr[last].r,mid+1,R,val);
}
int query(int rt,int last,int L,int R,int l,int r)
{
int sum=tr[rt].sum-tr[last].sum;
if(r<L || l>R || !sum) return 0;
if(L>=l && R<=r) return sum;
int mid=L+R>>1;
return query(tr[rt].l,tr[last].l,L,mid,l,r)+query(tr[rt].r,tr[last].r,mid+1,R,l,r);
}
int main()
{
freopen("delicious.in","r",stdin);
freopen("delicious.out","w",stdout);
scanf("%d%d",&n,&m);
int maxi=0;
for(int i=1;i<=n;i++) scanf("%d",&a[i]),maxi=max(maxi,a[i]);
int b,x,l,r;
for(int i=1;i<=n;i++)
update(root[i],root[i-1],0,maxi,a[i]);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d%d",&b,&x,&l,&r);
int ans=0,L,R,ok;
for(int k=18;k>=0;k--)
{
int dig=b&(1<<k);
if(dig && !query(root[l-1],root[r],0,maxi,ans-x,ans+(1<<k)-1-x)) ans+=(1<<k);
if(!dig && query(root[l-1],root[r],0,maxi,ans+(1<<k)-x,ans-x+(1<<(k+1))-1)) ans+=(1<<k);
}
printf("%d\n",ans^b);
}
return 0;
}
3.P2633 Count on a tree
【题意】在线求树上两点间点权值的第k小
【分析】利用差分思想,把每个点到根路径的值放在一颗权值线段树里,进而构成了主席树
所以对于u,v,我们求出lca,查询的时候利用s[u]+s[b]-s[lca]-s[f[lca]]在树上进行查询第k小即可
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int n,m,v[maxn],a[maxn],cnt;
int head[maxn],tot,root[maxn];
struct segtree
{
int l,r,sum;
}tr[maxn<<5];
struct edge
{
int to,nxt;
}e[maxn<<1];
void add(int x,int y)
{
e[++cnt].to=y; e[cnt].nxt=head[x]; head[x]=cnt;
}
int f[maxn][20],dep[maxn];
int ecnt;
void modify(int &rt,int last,int L,int R,int v)
{
if(!rt) rt=++ecnt;
if(L==R)
{
tr[rt].sum++;
return;
}
int mid=L+R>>1;
if(v<=mid) modify(tr[rt].l,tr[last].l,L,mid,v),tr[rt].r=tr[last].r;
else modify(tr[rt].r,tr[last].r,mid+1,R,v),tr[rt].l=tr[last].l;
tr[rt].sum=tr[tr[rt].l].sum+tr[tr[rt].r].sum;
}
int query(int x,int y,int c,int d,int L,int R,int val)
{
if(L==R) return L;
int mid=L+R>>1;
int sum=tr[tr[x].l].sum+tr[tr[y].l].sum-tr[tr[c].l].sum-tr[tr[d].l].sum;
// int sum=tr[x].sum+tr[y].sum-tr[c].sum-tr[d].sum;
if(sum>=val) return query(tr[x].l,tr[y].l,tr[c].l,tr[d].l,L,mid,val);
else return query(tr[x].r,tr[y].r,tr[c].r,tr[d].r,mid+1,R,val-sum);
}
void dfs(int x,int fa)
{
f[x][0]=fa; dep[x]=dep[fa]+1;
for(int i=head[x];i;i=e[i].nxt)
{
int to=e[i].to;
if(to==fa) continue;
modify(root[to],root[x],1,tot,a[to]);
dfs(to,x);
}
}
void lca_init()
{
for(int k=1;k<=18;k++)
for(int i=1;i<=n;i++)
f[i][k]=f[f[i][k-1]][k-1];
}
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=18;i>=0;i--)
if(dep[f[x][i]]>=dep[y])
x=f[x][i];
if(x==y) return x;
for(int i=18;i>=0;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
int main()
{
freopen("count.in","r",stdin);
freopen("count.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&v[i]),a[i]=v[i];
sort(v+1,v+n+1);
tot=unique(v+1,v+n+1)-v;
int x,y;
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
for(int i=1;i<=n;i++)
a[i]=lower_bound(v+1,v+tot+1,a[i])-v;
modify(root[1],root[0],1,tot,a[1]);
dfs(1,0); lca_init();
int ans=0,z;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
x^=ans;
int LCA=lca(x,y);
ans=v[query(root[x],root[y],root[LCA],root[f[LCA][0]],1,tot,z)];
printf("%d\n",ans);
}
return 0;
}