https://www.luogu.org/problemnew/show/P4137
只会log^2的带修主席树。。
看了题解,发现有高妙的一个log做法:权值线段树上,设数i对应的值ma[i]为数i首次出现的位置(没有出现就是n+1)
如果把询问按左端点排序,这样就转化为:修改:...;询问:询问[1,r]的答案
修改问题不大
询问[1,r]就转化为查询当前权值线段树上最小的数i,其对应的ma[i]>r;维护一下区间最大值,然后线段树上二分即可
可持久化一下线段树,还可以支持在线
。。。好吧可持久化的话有点卡空间,发现mex一定<=n,那么线段树可以少开一点
哎,还是思路不够灵活啊...
跑的极其慢?
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<vector> 5 #include<map> 6 using namespace std; 7 #define fi first 8 #define se second 9 #define mp make_pair 10 #define pb push_back 11 typedef long long ll; 12 typedef unsigned long long ull; 13 typedef pair<int,int> pii; 14 namespace S 15 { 16 const int N=8000000; 17 int maxn[N],lc[N],rc[N]; 18 int mem; 19 int getnode(int f) 20 { 21 int t=++mem; 22 maxn[t]=maxn[f];lc[t]=lc[f];rc[t]=rc[f]; 23 return t; 24 } 25 void setx(int L,int x,int l,int r,int &num) 26 { 27 num=getnode(num); 28 if(l==r) {maxn[num]=x;return;} 29 int mid=l+((r-l)>>1); 30 if(L<=mid) setx(L,x,l,mid,lc[num]); 31 else setx(L,x,mid+1,r,rc[num]); 32 maxn[num]=max(maxn[lc[num]],maxn[rc[num]]); 33 } 34 int getx(int R,int l,int r,int num) 35 { 36 if(l==r) return l; 37 int mid=l+((r-l)>>1); 38 if(maxn[lc[num]]>R) return getx(R,l,mid,lc[num]); 39 else return getx(R,mid+1,r,rc[num]); 40 } 41 } 42 int rt[200100]; 43 int a[200100],nxt[200100]; 44 int n,m; 45 map<int,int> ma; 46 const int ld=0,rd=200000; 47 int main() 48 { 49 int i,l,r; 50 S::maxn[0]=0x3f3f3f3f; 51 scanf("%d%d",&n,&m); 52 for(i=1;i<=n;i++) scanf("%d",&a[i]); 53 for(i=n;i>=1;i--) 54 { 55 nxt[i]=ma.count(a[i])?ma[a[i]]:n+1; 56 ma[a[i]]=i; 57 if(a[i]<=n) S::setx(a[i],i,ld,rd,rt[1]); 58 //printf("1a %d %d ",a[i],i); 59 } 60 for(i=2;i<=n;i++) 61 { 62 rt[i]=rt[i-1]; 63 if(a[i-1]<=n) S::setx(a[i-1],nxt[i-1],ld,rd,rt[i]); 64 //printf("%da %d %d ",i,a[i-1],nxt[i-1]); 65 } 66 while(m--) 67 { 68 scanf("%d%d",&l,&r); 69 printf("%d ",S::getx(r,ld,rd,rt[l])); 70 } 71 return 0; 72 }