pyyz Day7
可持久化数据结构
前置知识:
值域线段树
对每个数开桶
对桶开线段树
一般离散化
字典树
可持久化线段树
全局第k小:值域线段树二分
区间第k小
利用类似前缀和思想
开n个线段树
Tr-Tl-1进行操作
可持久化权值动态开点线段树二分(好抽象的板子)
主席树:可持久化权值线段树
不要开Long Long!!!
不要开Long Long!!!
不要开Long Long!!!
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define lson x<<1
#define rson x<<1|1
using namespace std;
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-48;ch=getchar();}
return x*f;
}
struct node{
int sum;
int l,r;
node(){
sum=l=r=0;
}
}tr[50000005];
int cnt;
void update(int x){
tr[x].sum=tr[tr[x].l].sum+tr[tr[x].r].sum;
}
int a[1000005],n,q,rt[1000005],lsh[1000005];
int build(int l,int r){
cnt++;
int x=cnt;
if(l==r){
tr[x].sum=a[l];
return x;
}
int mid=(l+r)>>1;
tr[x].l=build(l,mid);
tr[x].r=build(mid+1,r);
update(x);
return x;
}
int query(int nl,int nr,int r,int l,int k){
if(nl==nr) return nl;
int mid=(nl+nr)>>1;
int cn=tr[tr[r].l].sum-tr[tr[l].l].sum;
if(cn>=k) return query(nl,mid,tr[r].l,tr[l].l,k);
else return query(mid+1,nr,tr[r].r,tr[l].r,k-cn);
}
int modd(int l,int r,int x,int p,int v){
cnt++;
int pq=cnt;
tr[pq]=tr[x];
if(l==r){
tr[pq].sum+=v;
return pq;
}
int mid=(l+r)>>1;
if(p<=mid) tr[pq].l=modd(l,mid,tr[pq].l,p,v);
else tr[pq].r=modd(mid+1,r,tr[pq].r,p,v);
update(pq);
return pq;
}
signed main()
{
//freopen("filename.in", "r", stdin);
//freopen("filename.out", "w", stdout);
n=read(),q=read();
for(int i=1;i<=n;i++){
a[i]=read();
lsh[i]=a[i];
}
sort(lsh+1,lsh+n+1);
int le=unique(lsh+1,lsh+n+1)-lsh-1;
rt[0]=build(1,le);
for(int i=1;i<=n;i++){
int p=lower_bound(lsh+1,lsh+le+1,a[i])-lsh;
rt[i]=modd(1,le,rt[i-1],p,1);
}
for(int i=1;i<=q;i++){
int l=read(),r=read(),k=read();
int p=query(1,le,rt[r],rt[l-1],k);
cout<<lsh[p]<<'\n';
}
return 0;
}
T1 Count on a tree
考虑主席树+LCA
主席树维护每个点到根的链的信息
从fa[u]开新的线段树
答案是Tu+Tv-2*Tlca+lca
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define lson x<<1
#define rson x<<1|1
using namespace std;
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-48;ch=getchar();}
return x*f;
}
struct node{
int sum;
int l,r;
node(){
sum=l=r=0;
}
}tr[5000005];
int cnt;
void update(int x){
tr[x].sum=tr[tr[x].l].sum+tr[tr[x].r].sum;
}
int a[100005],n,q,rt[100005],lsh[100005];
vector<int> tu[100005];
int dep[500006],f[500005][25],le;
int build(int l,int r){
cnt++;
int x=cnt;
if(l==r){
tr[x].sum=a[l];
return x;
}
int mid=(l+r)>>1;
tr[x].l=build(l,mid);
tr[x].r=build(mid+1,r);
update(x);
return x;
}
int modd(int l,int r,int x,int p,int v){
cnt++;
int pq=cnt;
tr[pq]=tr[x];
if(l==r){
tr[pq].sum+=v;
return pq;
}
int mid=(l+r)>>1;
if(p<=mid) tr[pq].l=modd(l,mid,tr[pq].l,p,v);
else tr[pq].r=modd(mid+1,r,tr[pq].r,p,v);
update(pq);
return pq;
}
void dfs(int x,int fa){
int qq=lower_bound(lsh+1,lsh+le+1,a[x])-lsh;
rt[x]=modd(1,le,rt[fa],qq,1);
dep[x]=dep[fa]+1;
for(int i=0;i<=20;i++){
f[x][i+1]=f[f[x][i]][i];
}
for(auto ed:tu[x]){
if(ed==fa) continue;
f[ed][0]=x;
dfs(ed,x);
}
}
int lca(int x,int y){
if(dep[x]>dep[y]) swap(x,y);
for(int i=20;i>=0;i--){
if(dep[f[y][i]]>=dep[x]) y=f[y][i];
}
if(x==y) return x;
for(int i=20;i>=0;i--){
if(f[x][i]!=f[y][i]){
x=f[x][i];
y=f[y][i];
}
}
return f[y][0];
}
int query(int nl,int nr,int r,int l,int k,int lc,int lcaa){
if(nl==nr) return nl;
int mid=(nl+nr)>>1;
int cn=tr[tr[r].l].sum+tr[tr[l].l].sum-tr[tr[lc].l].sum-tr[tr[lcaa].l].sum;
if(cn>=k) return query(nl,mid,tr[r].l,tr[l].l,k,tr[lc].l,tr[lcaa].l);
else return query(mid+1,nr,tr[r].r,tr[l].r,k-cn,tr[lc].r,tr[lcaa].r);
}
signed main()
{
//freopen("filename.in", "r", stdin);
//freopen("filename.out", "w", stdout);
n=read(),q=read();
for(int i=1;i<=n;i++){
a[i]=read();
lsh[i]=a[i];
}
sort(lsh+1,lsh+n+1);
le=unique(lsh+1,lsh+n+1)-lsh-1;
for(int i=1;i<n;i++){
int u=read(),v=read();
tu[u].push_back(v);
tu[v].push_back(u);
}
rt[0]=build(1,le);
dfs(1,0);
int las=0;
for(int i=1;i<=q;i++){
int l=read(),r=read(),k=read();
las^=l;
int lc=lca(las,r);
int p=query(1,le,rt[r],rt[las],k,rt[lc],rt[f[lc][0]]);
cout<<lsh[p]<<'\n';
las=lsh[p];
}
return 0;
}
T2 二维数点
主席树做法(强制在线):
把所有点一起离散化
对每个横坐标,记录每个纵坐标出现的次数
答案T x2 [y2-y1]- T x1-1 [y2-y1]
T3 标记永久化(区间加,访问历史区间和)
T4 Dynamic Rankings
树状数组套主席树
树状数组记录主席树根节点
T5 [SDOI2009] HH的项链
转化成二维数点
记录prei表示ai上次出现的位置
限制转化成要求 l<=i<=r且0<=prei<=l-1
发现和二维数点没区别
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define lson x<<1
#define rson x<<1|1
using namespace std;
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-48;ch=getchar();}
return x*f;
}
struct node{
int sum;
int l,r;
node(){
sum=l=r=0;
}
}tr[30000005];
int cnt;
void update(int x){
tr[x].sum=tr[tr[x].l].sum+tr[tr[x].r].sum;
}
int a[1000005],n,q,rt[1000005],pre[1000005],mmq[1000005];
int build(int l,int r){
cnt++;
int x=cnt;
if(l==r){
tr[x].sum=1;
return x;
}
int mid=(l+r)>>1;
tr[x].l=build(l,mid);
tr[x].r=build(mid+1,r);
update(x);
return x;
}
int query(int l,int r,int x,int nl,int nr){
if(nl<=l&&r<=nr) return tr[x].sum;
int mid=(l+r)>>1;
if(nl<=mid){
if(mid<nr) return query(l,mid,tr[x].l,nl,nr)+query(mid+1,r,tr[x].r,nl,nr);
else return query(l,mid,tr[x].l,nl,nr);
}
return query(mid+1,r,tr[x].r,nl,nr);
}
int modd(int l,int r,int x,int p,int v){
cnt++;
int pq=cnt;
tr[pq]=tr[x];
if(l==r){
tr[pq].sum+=v;
return pq;
}
int mid=(l+r)>>1;
if(p<=mid) tr[pq].l=modd(l,mid,tr[pq].l,p,v);
else tr[pq].r=modd(mid+1,r,tr[pq].r,p,v);
update(pq);
return pq;
}
signed main()
{
n=read();
for(int i=1;i<=n;i++){
a[i]=read();
}
for(int i=1;i<=n;i++){
pre[i]=mmq[a[i]];
mmq[a[i]]=i;
}
rt[0]=build(0,n);
for(int i=1;i<=n;i++){
rt[i]=modd(0,n,rt[i-1],pre[i],1);
}
q=read();
for(int i=1;i<=q;i++){
int l=read(),r=read();
int sum=query(0,n,rt[r],0,l-1)-query(0,n,rt[l-1],0,l-1);
cout<<sum<<'\n';
}
return 0;
}
T6 Mishka and Interesting sum
答案=区间异或和^区间去重后的异或和
显然前一项可以前缀异或和进行计算
后一项可以转化成二维数点
记录prei表示ai上次出现的位置
限制转化成要求 l<=i<=r且0<=prei<=l-1
(和HH的项链一模一样)
无需离散化
注:n以内的若干个数随便选进行异或,和<=2*n
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define lson x<<1
#define rson x<<1|1
using namespace std;
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-48;ch=getchar();}
return x*f;
}
struct node{
int sum;
int l,r;
node(){
sum=l=r=0;
}
}tr[50000005];
int cnt;
void update(int x){
tr[x].sum=tr[tr[x].l].sum^tr[tr[x].r].sum;
}
int a[1000005],n,q,rt[1000005],pre[1000005],su[100005];
map<int,int> mmq;
int build(int l,int r){
cnt++;
int x=cnt;
if(l==r){
tr[x].sum=a[l];
return x;
}
int mid=(l+r)>>1;
tr[x].l=build(l,mid);
tr[x].r=build(mid+1,r);
update(x);
return x;
}
int query(int l,int r,int x,int nl,int nr){
if(nl<=l&&r<=nr) return tr[x].sum;
int mid=(l+r)>>1;
if(nl<=mid){
if(mid<nr) return query(l,mid,tr[x].l,nl,nr)^query(mid+1,r,tr[x].r,nl,nr);
else return query(l,mid,tr[x].l,nl,nr);
}
return query(mid+1,r,tr[x].r,nl,nr);
}
int modd(int l,int r,int x,int p,int v){
cnt++;
int pq=cnt;
tr[pq]=tr[x];
if(l==r){
tr[pq].sum^=v;
return pq;
}
int mid=(l+r)>>1;
if(p<=mid) tr[pq].l=modd(l,mid,tr[pq].l,p,v);
else tr[pq].r=modd(mid+1,r,tr[pq].r,p,v);
update(pq);
return pq;
}
signed main()
{
n=read();
for(int i=1;i<=n;i++){
a[i]=read();
su[i]^=su[i-1];
su[i]^=a[i];
}
for(int i=1;i<=n;i++){
pre[i]=mmq[a[i]];
mmq[a[i]]=i;
}
rt[0]=build(0,n);
for(int i=1;i<=n;i++){
rt[i]=modd(0,n,rt[i-1],pre[i],a[i]);
}
q=read();
for(int i=1;i<=q;i++){
int l=read(),r=read();
int sum=query(0,n,rt[r],0,l-1)^query(0,n,rt[l-1],0,l-1);
sum^=su[r];
sum^=su[l-1];
cout<<sum<<'\n';
}
return 0;
}
T7 [省选联考 2021 A/B 卷] 宝石
考虑u,v节点之间的路径是一条链
定义l为lca(u,v)
对于u~l倍增,假设跳到pk
对于l~v,考虑将从上往下跳转化成从下往上跳
二分答案x,考虑Px是否可跳到Pk且不超过l
可持久化数组记录Px向上跳最近跳到那个点+倍增
可持久化数组从fa转移
只有wu的点的信息变成u,其余不变
实现比较困难
超级倍增题
可持久化字典树
仿可持久化线段树
高位贪心
建字典树
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define jiaa(a,b) {a+=b;if(a>=MOD) a-=MOD;}
#define jian(a,b) {a-=b;if(a<0) a+=MOD;}
using namespace std;
int ksm(int a,int b,int p){
if(b==0) return 1;
if(b==1) return a%p;
int c=ksm(a,b/2,p);
c=c*c%p;
if(b%2==1) c=c*a%p;
return c%p;
}
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-48;ch=getchar();}
return x*f;
}
int n,q;
int tr[20000005][2],en[20000005];
int a[600005],rt[600005],cnt;
void gai(int x,int k,int l,int r){
if(k<0){
en[r]=x;
return ;
}
int kk=a[x]>>k&1;
tr[r][kk]=++cnt;
if(l) tr[r][kk^1]=tr[l][kk^1];
gai(x,k-1,tr[l][kk],tr[r][kk]);
en[r]=max(en[tr[r][0]],en[tr[r][1]]);
}
int query(int x,int k,int v,int dw){
if(k<0) return a[en[x]]^v;
int kk=v>>k&1;
if(en[tr[x][kk^1]]>=dw) return query(tr[x][kk^1],k-1,v,dw);
return query(tr[x][kk],k-1,v,dw);
}
signed main()
{
//freopen("filename.in", "r", stdin);
//freopen("filename.out", "w", stdout);
n=read(),q=read();
rt[0]=++cnt;
en[0]=-1;
gai(0,23,-1,rt[0]);
for(int i=1;i<=n;i++){
a[i]=read();
a[i]^=a[i-1];
rt[i]=++cnt;
gai(i,23,rt[i-1],rt[i]);
}
while(q--){
char opt;
cin>>opt;
if(opt=='A'){
int x=read();
a[++n]=x;
a[n]^=a[n-1];
rt[n]=++cnt;
gai(n,23,rt[n-1],rt[n]);
}
else{
int l=read(),r=read();
int x=read();
cout<<query(rt[r-1],23,x^a[n],l-1)<<'\n';
}
}
return 0;
}

浙公网安备 33010602011771号