P3567 [POI2014]KUR-Couriers
题意
输出区间内出现次数严格大于区间长度的一半的数,存在输出任意,否则输出0。
思路
区间数出现次数具有可减性,考虑建立主席树,令 k = ( r − l + 1 ) 2 k=\dfrac{(r-l+1)}{2} k=2(r−l+1),每次查询左子树元素个数是否大于 k k k,是就递归进入左子树,否则进入右子树,每次都需要提前判断当前区间元素个数是否大于 k k k,否则直接返回 0 0 0。
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
空间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=5e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define IOS ios::sync_with_stdio(false),cin.tie(0)
void Print(int *a,int n){
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\n",a[n]);
}
int rt[N*20];
struct PST{
#define lx a[x].l
#define rx a[x].r
struct node{
int l,r,s;
}a[N*20];
int cnt;
void upd(int &x,int pre,int l,int r,int v){
x=++cnt,a[x]=a[pre],a[x].s++;
if(l==r) return;
int m=l+r>>1;
if(v<=m) upd(lx,a[pre].l,l,m,v);
else upd(rx,a[pre].r,m+1,r,v);
}
int que(int k1,int k2,int l,int r,int k){
if(a[k2].s-a[k1].s<=k) return 0;
if(l==r) return l;
int m=l+r>>1;
if(a[a[k2].l].s-a[a[k1].l].s>k) return que(a[k1].l,a[k2].l,l,m,k);
else return que(a[k1].r,a[k2].r,m+1,r,k);
}
}T;
int a[N],b[N];
void solve(){
//think twice code once
int n,m,q;scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];
sort(b+1,b+n+1);
m=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++){
int x=lower_bound(b+1,b+m+1,a[i])-b;
T.upd(rt[i],rt[i-1],1,m,x);
}
while(q--){
int l,r;scanf("%d%d",&l,&r);
int k=(r-l+1)>>1;
printf("%d\n",b[T.que(rt[l-1],rt[r],1,m,k)]);
}
}
int main(){
solve();
return 0;
}
P3939 数颜色
题意
区间查询某个数出现次数,相连数交换。
思路
主席树,相邻数交换,第 x + 1 x+1 x+1版本不用修改,第 x x x版本先删去 a [ x ] a[x] a[x],然后再加上 a [ x + 1 ] a[x+1] a[x+1]即可。
code
// Problem: P3939 数颜色
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3939
// Memory Limit: 250 MB
// Time Limit: 1000 ms
// Date: 2021-04-04 12:50:36
// --------by Herio--------
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=3e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define IOS ios::sync_with_stdio(false),cin.tie(0)
void Print(int *a,int n){
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\n",a[n]);
}
int rt[N*80],ls[N*80],rs[N*80],s[N*80],cnt;
int a[N];
void upd(int &x,int y,int l,int r,int k,int v){
x=++cnt,ls[x]=ls[y],rs[x]=rs[y],s[x]=s[y]+v;
if(l==r) return;
int m=l+r>>1;
if(k<=m) upd(ls[x],ls[y],l,m,k,v);
else upd(rs[x],rs[y],m+1,r,k,v);
}
int que(int x,int y,int l,int r,int k){
if(l==r) return s[y]-s[x];
int m=l+r>>1;
if(k<=m) return que(ls[x],ls[y],l,m,k);
else return que(rs[x],rs[y],m+1,r,k);
}
void solve(){
//think twice code once
int n,q;
scanf("%d%d",&n,&q);
int mx=3e5;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
upd(rt[i],rt[i-1],1,mx,a[i],1);
}
while(q--){
int op;
scanf("%d",&op);
if(op==1){
int l,r,c;
scanf("%d%d%d",&l,&r,&c);
printf("%d\n",que(rt[l-1],rt[r],1,mx,c));
}
else {
int x;scanf("%d",&x);
upd(rt[x],rt[x],1,mx,a[x],-1);
upd(rt[x],rt[x],1,mx,a[x+1],1);
swap(a[x],a[x+1]);
}
}
return;
}
int main(){
solve();
return 0;
}
2021ICPC昆明-M-Stone Games
题意
求区间最小不能表示的数,对于一个区间每个数至多使用一次。
思路
主席树,考虑一个问题,对于假设当前能表示的范围是: [ 1 , x ] [1,x] [1,x],我们求出所有数范围在 [ 1 , x + 1 ] [1,x+1] [1,x+1]的和 s u m sum sum,则当前能表示的范围是 [ 1 , s u m ] [1,sum] [1,sum]。
为什么 [ 1 , x + 1 ] [1,x+1] [1,x+1]的右端点是 x + 1 x+1 x+1呢?
假设当前只加上一个数 a a a。
则利用该 a a a,可表示的范围是: [ a + 1 , x + a ] [a+1,x+a] [a+1,x+a]。
a + 1 ≤ x + 1 a+1 \le x+1 a+1≤x+1,都是满足 [ 1 , x + a ] [1,x+a] [1,x+a]的。
即 a ≤ x a\le x a≤x满足。
当 a = x + 1 a=x+1 a=x+1也满足,因为 a = x + 1 a=x+1 a=x+1可表示自身。
这样范围就是 [ 1 , x ] [ x + 1 , x + 1 ] , [ x + 2 , x + a ] = [ 1 , x + a ] [1,x][x+1,x+1],[x+2,x+a]=[1,x+a] [1,x][x+1,x+1],[x+2,x+a]=[1,x+a]
当 a > x + 1 a>x+1 a>x+1时 表示不了 x + 1 x+1 x+1。所以答案就是 x + 1 x+1 x+1了,否则令 x = x + a x=x+a x=x+a。
然后利用主席树,进行区间求和 [ 1 , x ] [1,x] [1,x]。为什么要用主席树呢,因为要表示权值 [ 1 , 1 e 9 ] [1,1e9] [1,1e9]范围,动态开点避免爆内存。
注意到每次更新 x x x , x x x至少增加一倍。所以复杂度是 O ( n l o g n ) O(nlogn) O(nlogn)
code
// Problem: Stone Games
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/12548/M
// Memory Limit: 2097152 MB
// Time Limit: 8000 ms
// Date: 2021-04-04 14:56:37
// --------by Herio--------
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=1e6+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define IOS ios::sync_with_stdio(false),cin.tie(0)
void Print(int *a,int n){
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\n",a[n]);
}
int n,q,mx=1e9;
ll lans;
int ls[N*32],rs[N*32],rt[N*32],cnt;
ll s[N*32];
void upd(int x,int &y,int l,int r,int v){
y=++cnt,ls[y]=ls[x],rs[y]=rs[x],s[y]=s[x]+v;
if(l==r) return;
int m=l+r>>1;
if(v<=m) upd(ls[x],ls[y],l,m,v);
else upd(rs[x],rs[y],m+1,r,v);
}
ll que(int x,int y,int l,int r,int ql,int qr){
if(l>=ql&&r<=qr) return s[y]-s[x];
if(l>r) return 0;
ll ans=0;
int m=l+r>>1;
if(ql<=m) ans+=que(ls[x],ls[y],l,m,ql,qr);
if(qr>m) ans+=que(rs[x],rs[y],m+1,r,ql,qr);
return ans;
}
void solve(){
//think twice code once
scanf("%d%d",&n,&q);
for(int i=1,x;i<=n;i++){
scanf("%d",&x);
upd(rt[i-1],rt[i],1,mx,x);
}
while(q--){
int l,r;
scanf("%d%d",&l,&r);
l=(l+lans)%n+1,r=(r+lans)%n+1;
if(l>r) swap(l,r);
ll ans=0;
while(1){
ll s=que(rt[l-1],rt[r],1,mx,1,min(1LL*mx,ans+1));
if(s<=ans) break;
ans=s;
}
lans=ans+1;
printf("%lld\n",lans);
}
}
int main(){
solve();
return 0;
}
P4137 Rmq Problem / mex
区间MEX问题
思路
主席树,需要记录一下每个数最后出现的位置,然后区间维护每个数最后出现的位置的最小值。最后查询的时候就查询位置小于 l l l的最小权值,注意到只有 n n n个数,所以答案不会大于 n n n,对于大于 n n n的数直接 r t [ i ] = r t [ i − 1 ] rt[i]=rt[i-1] rt[i]=rt[i−1]即可,因为有 0 0 0存在,为了方便用权值线段树储存,我们就都先加1,范围就是 [ 1 , n + 1 ] [1,n+1] [1,n+1],然后最后答案再减1即可。
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=2e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define IOS ios::sync_with_stdio(false),cin.tie(0)
void Print(int *a,int n){
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\n",a[n]);
}
int cnt,ls[N*20],rs[N*20],s[N*20],rt[N*20];
void upd(int x,int &y,int l,int r,int p,int v){
y=++cnt,ls[y]=ls[x],rs[y]=rs[x];
if(l==r) {s[y]=v;return;}
int m=l+r>>1;
if(p<=m) upd(ls[x],ls[y],l,m,p,v);
else upd(rs[x],rs[y],m+1,r,p,v);
s[y]=min(s[ls[y]],s[rs[y]]);
}
int que(int x,int ql,int l,int r){
if(l==r) return l;
int m=l+r>>1;
if(s[ls[x]]<ql) return que(ls[x],ql,l,m);
else return que(rs[x],ql,m+1,r);
}
void solve(){
//think twice code once
int n,m;
scanf("%d%d",&n,&m);
for(int i=1,x;i<=n;i++){
scanf("%d",&x);
x++;
if(x>n) rt[i]=rt[i-1];
else upd(rt[i-1],rt[i],1,n+1,x,i);
}
while(m--){
int l,r;scanf("%d%d",&l,&r);
printf("%d\n",que(rt[r],l,1,n+1)-1);
}
}
int main(){
solve();
return 0;
}