题解:P12485 [集训队互测 2024] PM 大师
P12485 [集训队互测 2024] PM 大师题解
tip
date:2025/7/17,DAY11
本题是模拟赛 T4,Clonoth 也是直接把他集训队的题搬来了,不过神奇的是题解就一篇,提交人数达惊人的
本人迫不得已只能把官方题解改了一下搬来了。思路不是我的,希望 Clonoth 不会发现。
本题其实难在思考过程,如果真弄懂了代码其实不难,树状数组和线段树都比较板(特指黑题),但真的好难理解啊啊啊。存好大脑,开始了。
以下认为
做法 1
考虑
按照
时间复杂度
code
蒟蒻的考场代码。
while(q--){
cin>>x>>k>>y;
a[x]=k;
if(a[y]!=0){
cout<<a[y]<<'\n';
continue;
}
for(int j=1;j<=n;j++)
fa[j]=j;
for(int j=1;j<=n;j++){
if(a[j]==-1) continue;
if(a[j]==0){
int b=find(1);
if(j==y){
cout<<b<<'\n';
break;
}
fa[find(b)]=find(b+1);
continue;
}
fa[find(a[j])]=find(a[j]+1);
}
}
并查集的作用就是 vis,已经出现过的和 悲。
做法 2
考虑解决子任务
注意到存在集合
此时
使用树状数组或平衡树等数据结构维护插入、删除、求
code
来自 HZ 巨佬的代码 orz:
void add(int x,int k){
for(int i=x;i<=n;i+=i&-i)
tr[i]+=k;
return;
}
int qry(int x) {
int res=0;
while(x){
res+=tr[x];
x-=x&-x;
}
return res;
}
int K=n+1;
for(int i=1;i<=n;i++)
if(a[i]==0){
K=i;
break;
}
for(int i=1;i<=q;i++){
if(a[x[i]]!=-1){
cnt[a[x[i]]]--;
if(!cnt[a[x[i]]])
add(a[x[i]],-1);
}
if(k[i]!=-1){
if(!cnt[k[i]])
add(k[i],1);
cnt[k[i]]++;
}
a[x[i]]=k[i];
if(y[i]<K) cout<<a[y[i]]<<'\n';
else{
int l=1,r=n;
while(l<r){
int mid=(l+r)>>1;
if (mid-qry(mid)<y[i]-K+1) l=mid+1;
else r=mid;
}
cout<<l<<'\n';
}
}
做法 3
考虑解决子任务
不难注意到,满足做法
以下我们假设 update 操作的意义)。由此,定义
在任何时刻,对于
但在一次修改操作中,可能会引起需要往
那么每次修改对
tip
文中
code
inline void insert_check(int x){
int k=a[x];
if(pos[k].top()!=x)
return;
int u=b.kth(pre[x]);
if(u<k){
if(!in[k]){
if(k<n){
int i=f.find(1,1,n,k+1,n);
if(i){
f.modify(1,1,n,i,inf);
g.modify(1,1,n,i,0);
b.add(i,1);
in[i]=false;
++X;
}
else
i=n+1;
if(i<=n)
Sum+=i-k;
if(k+1<i)
g.update(1,1,n,k+1,i-1,1);
}
b.add(k,-1);
in[k]=1;
}
f.modify(1,1,n,k,b.ask(k)-pre[x]);
g.modify(1,1,n,k,inf);
}
else{
g.modify(1,1,n,k,pre[x]-b.ask(k));
f.modify(1,1,n,k,inf);
}
return;
}
inline void insert(int x,int k){
a[x]=k;
if(k==-1)
return;
pos[k].insert(x);
insert_check(x);
return;
}
做法 4
首先原问题等价于在初始全为
注意到在做法
对称的,对于
修改的形式也是类似的。但值得注意的是,在对
使用线段树维护
code
inline void erase(int x){
int k=a[x];
if(k==-1)
return;
pos[k].erase(x);
if(!pos[k].empty()&&pos[k].top()<x)
return;
if(in[k]){
if(k<n){
int i=g.find(1,1,n,k+1,n);
if(i){
f.modify(1,1,n,i,0);
g.modify(1,1,n,i,inf);
b.add(i,-1);
in[i]=true;
++Y;
}
else
i=n+1;
if(i<=n)
Sum+=i-k;
if(k+1<i)
f.update(1,1,n,k+1,i-1,1);
}
in[k]=false;
b.add(k,1);
}
f.modify(1,1,n,k,inf);
g.modify(1,1,n,k,inf);
if(!pos[k].empty())
insert_check(pos[k].top());
return;
}
重构过的代码
封装好了,有需自取。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10,inf=1e9;
int n,m;
int a[N],pre[N];
bool in[N];
struct heap{
priority_queue<int,vector<int>,greater<int>> p,q;
inline void insert(int x){
p.push(x);
return;
}
inline void erase(int x){
q.push(x);
return;
}
inline void update(){
while(!p.empty()&&!q.empty()&&p.top()==q.top())
p.pop(),q.pop();
return;
}
inline bool empty(){
update();
return p.empty();
}
inline int top(){
update();
return p.top();
}
inline void clear(){
while(!p.empty())
p.pop();
while(!q.empty())
q.pop();
return;
}
}pos[N];
struct BIT{
int bit[N];
inline int lowbit(int x){
return x&-x;
}
inline void add(int x,int k){
for(int i=x;i<=n;i+=lowbit(i))
bit[i]+=k;
return;
}
inline int ask(int x){
int ans=0;
for(int i=x;i;i-=lowbit(i))
ans+=bit[i];
return ans;
}
inline int kth(int k){
if(!k) return 0;
int i=0,s=0;
for(int w=log2(n);w!=-1;--w){
int j=i+(1<<w);
if(j<=n&&s+bit[j]<k)
s+=bit[j],i=j;
}
return i+1;
}
}b;
struct segment_tree{
static const int Size=(1<<21)+10;
int sum[Size],tag[Size];
inline void push_up(int p){
sum[p]=min(sum[p<<1],sum[p<<1|1]);
return;
}
inline void push_down(int p){
if(tag[p]){
sum[p<<1]+=tag[p];
sum[p<<1|1]+=tag[p];
tag[p<<1]+=tag[p];
tag[p<<1|1]+=tag[p];
tag[p]=0;
}
return;
}
int find(int p,int l,int r,int x,int y){
if(r<x||l>y) return 0;
if(x<=l&&r<=y&&sum[p]){
sum[p]--;
tag[p]--;
return 0;
}
if(l==r) return l;
push_down(p);
int mid=l+r>>1;
int ans=0;
ans+=find(p<<1,l,mid,x,y);
if(ans) return ans;
ans+=find(p<<1|1,mid+1,r,x,y);
return ans;
}
void update(int p,int l,int r,int x,int y,int k){
if(l>y||r<x) return;
if(x<=l&&r<=y){
sum[p]+=k;
tag[p]+=k;
return;
}
push_down(p);
int mid=l+r>>1;
update(p<<1,l,mid,x,y,k);
update(p<<1|1,mid+1,r,x,y,k);
push_up(p);
return;
}
void modify(int p,int l,int r,int x,int k){
if(r<x||l>x) return;
if(l==r){
sum[p]=k;
return;
}
push_down(p);
int mid=l+r>>1;
modify(p<<1,l,mid,x,k);
modify(p<<1|1,mid+1,r,x,k);
push_up(p);
return;
}
void build(int p,int l,int r){
tag[p]=0;
if(l==r){
sum[p]=inf;
return;
}
int mid=l+r>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
push_up(p);
return;
}
}f,g;
int X,Y;
long long Sum=0;
inline void insert_check(int x){
int k=a[x];
if(pos[k].top()!=x)
return;
int u=b.kth(pre[x]);
if(u<k){
if(!in[k]){
if(k<n){
int i=f.find(1,1,n,k+1,n);
if(i){
f.modify(1,1,n,i,inf);
g.modify(1,1,n,i,0);
b.add(i,1);
in[i]=0;
++X;
}
else
i=n+1;
if(i<=n)
Sum+=i-k;
if(k+1<i)
g.update(1,1,n,k+1,i-1,1);
}
b.add(k,-1);
in[k]=1;
}
f.modify(1,1,n,k,b.ask(k)-pre[x]);
g.modify(1,1,n,k,inf);
}
else{
g.modify(1,1,n,k,pre[x]-b.ask(k));
f.modify(1,1,n,k,inf);
}
return;
}
inline void insert(int x,int k){
a[x]=k;
if(k==-1)
return;
pos[k].insert(x);
insert_check(x);
return;
}
inline void erase(int x){
int k=a[x];
if(k==-1)
return;
pos[k].erase(x);
if(!pos[k].empty()&&pos[k].top()<x)
return;
if(in[k]){
if(k<n){
int i=g.find(1,1,n,k+1,n);
if(i){
f.modify(1,1,n,i,0);
g.modify(1,1,n,i,inf);
b.add(i,-1);
in[i]=1;
++Y;
}
else
i=n+1;
if(i<=n)
Sum+=i-k;
if(k+1<i)
f.update(1,1,n,k+1,i-1,1);
}
in[k]=0;
b.add(k,1);
}
f.modify(1,1,n,k,inf);
g.modify(1,1,n,k,inf);
if(!pos[k].empty())
insert_check(pos[k].top());
return;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;++i){
cin>>a[i];
pre[i]=pre[i-1]+!a[i];
}
f.build(1,1,n);
g.build(1,1,n);
for(int i=1;i<=n;++i)
b.add(i,1);
int x,k,y;
while(m--){
cin>>x>>k>>y;
erase(x);
insert(x,k);
if(a[y]!=0) cout<<a[y]<<'\n';
if(a[y]==0) cout<<b.kth(pre[y])<<'\n';
}
#define _ 0
return ~~(0^_^0);
}
完结散花!!!