题目:
给定一个长度为
n
n
n的序列
a
a
a,有两种操作:
(1)
1
l
r
k
1\ l \ r \ k
1 l r k,问
[
l
,
r
]
[l,r]
[l,r]区间内是否存在
k
k
k个不同的数,如果存在,设为
x
1...
k
x_{1...k}
x1...k,其中
x
i
x_i
xi出现的次数为
c
n
t
i
cnt_i
cnti,问最小的
d
i
f
dif
dif,使任意的
i
,
j
(
1
≤
i
,
j
≤
k
)
i,j(1 \le i,j \le k)
i,j(1≤i,j≤k),满足
∣
c
n
t
i
−
c
n
t
j
∣
≤
d
i
f
|cnt_i-cnt_j| \le dif
∣cnti−cntj∣≤dif
(2)
2
p
x
2\ p \ x
2 p x,令
a
p
=
x
a_p=x
ap=x
(
1
≤
n
,
k
,
a
i
,
x
≤
1
0
5
,
1
≤
l
,
r
,
p
≤
n
)
(1 \le n ,k,a_i,x \le 10^5,1 \le l,r,p \le n)
(1≤n,k,ai,x≤105,1≤l,r,p≤n)
题解:
用带修莫队,弱化版可见P1903 [国家集训队]数颜色 / 维护队列
在P1903中,我们已经维护出了每种数
x
x
x出现的次数
c
n
t
x
cnt_x
cntx。在这个基础上,我们对于每种出现次数
x
x
x,维护
c
c
n
t
x
ccnt_x
ccntx,表示有
c
c
n
t
x
ccnt_x
ccntx个数出现了
x
x
x次,处理每次询问时,我们将
c
c
n
t
x
ccnt_x
ccntx为正的
x
x
x取出来,从小到大排序后,用双指针框出当前询问的
k
k
k,然后更新答案。如果
c
c
n
t
ccnt
ccnt是用数组实现的,那么取出为正的位置需要
O
(
n
)
O(n)
O(n),显然不行,所以考虑用链表实现,在链表中的就是所有
c
c
n
t
ccnt
ccnt为正的位置。考虑这个链表有多长,一共
n
n
n个数,那么最长的情况就是1->2->3->4->…->
l
e
n
len
len,那么显然
l
e
n
len
len是
O
(
n
)
O(\sqrt n)
O(n)级别的,足以通过此题。(时限6s确实可以为所欲为,这个算法不是本题的最优方法)
复杂度: O ( n m 2 3 + n n l o g n ) O(nm^{\frac{2}{3}}+n\sqrt n log\sqrt n) O(nm32+nnlogn)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;
#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=1e6+5;
ll read(){
ll 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;
}
int n,m,blo,tot,qnum,cnum,head,base;
int a[maxn],cnt[maxn],address[maxn],ans[maxn],bl[maxn];
pii b[maxn];
queue<int>que;
struct qry{
int l,r,t,id,k;
}q[maxn];
struct change{
int pos,val;
}c[maxn];
struct list_node{
int v,nxt,cnt,prev;
}list[maxn];
int cmp(qry a,qry b){
if(bl[a.l]==bl[b.l]){
if(bl[a.r]==bl[b.r]){
return a.t<b.t;
}
return bl[a.r]<bl[b.r];
}
return bl[a.l]<bl[b.l];
}
int newnode(){
int res;
if(!que.empty()){
res=que.front();
que.pop();
}
else{
res=++tot;
}
list[res].nxt=list[res].prev=-1;
return res;
}
void ldel(int x){
if(x<=0)return;
int p=address[x];
list[p].cnt--;
if(list[p].cnt==0){
list[list[p].prev].nxt=list[p].nxt;
if(list[p].nxt!=-1)list[list[p].nxt].prev=list[p].prev;
que.push(p);
address[x]=-1;
}
}
void ladd(int x){
if(x<=0)return;
int p=address[x];
if(p==-1){
p=address[x]=newnode();
list[p].prev=head;
list[p].nxt=list[head].nxt;
list[head].nxt=p;
if(list[p].nxt!=-1)list[list[p].nxt].prev=p;
list[p].v=x;
list[p].cnt=0;
}
list[p].cnt++;
}
void add(int p){
ldel(cnt[a[p]]);
cnt[a[p]]++;
ladd(cnt[a[p]]);
}
void del(int p){
ldel(cnt[a[p]]);
cnt[a[p]]--;
ladd(cnt[a[p]]);
}
void work(int p,int i){
if(q[i].l<=c[p].pos&&c[p].pos<=q[i].r){
del(c[p].pos);
swap(a[c[p].pos],c[p].val);
add(c[p].pos);
}
else{
swap(a[c[p].pos],c[p].val);
}
}
int cmp2(pii a,pii b){
return a.fi<b.fi;
}
int main(void){
// freopen("in.txt","r",stdin);
scanf("%d%d",&n,&m);
blo=(int)pow(n,2.0/3);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
bl[i]=(i-1)/blo+1;
}
cnum=qnum=0;
int op;
for(int i=1;i<=m;i++){
scanf("%d",&op);
if(op==1){
++qnum;
scanf("%d%d%d",&q[qnum].l,&q[qnum].r,&q[qnum].k);
q[qnum].id=qnum;
q[qnum].t=cnum;
}
else{
++cnum;
scanf("%d%d",&c[cnum].pos,&c[cnum].val);
}
}
sort(q+1,q+qnum+1,cmp);
int l=1,r=0,t=0;
tot=0;
head=newnode();
for(int i=0;i<=n;i++){
address[i]=-1;
}
for(int i=1;i<=qnum;i++){
while(l<q[i].l)del(l++);
while(l>q[i].l)add(--l);
while(r<q[i].r)add(++r);
while(r>q[i].r)del(r--);
while(t<q[i].t)work(++t,i);
while(t>q[i].t)work(t--,i);
int num=0;
for(int p=list[head].nxt;p!=-1;p=list[p].nxt){
b[++num].fi=list[p].v;
b[num].se=list[p].cnt;
}
sort(b+1,b+num+1,cmp2);
int sz=0,tp=0,mn=INF;
for(int j=1;j<=num;j++){
while(tp<num&&sz<q[i].k){
++tp;
sz+=b[tp].se;
}
if(sz<q[i].k)break;
mn=min(mn,b[tp].fi-b[j].fi);
sz-=b[j].se;
}
if(mn==INF)ans[q[i].id]=-1;
else ans[q[i].id]=mn;
}
for(int i=1;i<=qnum;i++){
printf("%d\n",ans[i]);
}
return 0;
}