Codeforces Round #668 (Div. 2)E. Fixed Point Removal ##
题意
给你一个序列a,若 a [ i ] = = i a[i] == i a[i]==i你可以删除 a [ i ] a[i] a[i],删除后前后两部分将重新连接,序列a的权值为他能最多能删除的次数
给你q个查询,每个查询给你个x,y,表示将前x个元素和后个元素变为n+1,a的权值变为了多少
思路
首先我们对原序列分析,要是删除的次数最多,我们肯定是优先删除i大的,口胡证明若a,b位置可删除,a<b,先删除a的会影响a到n的可操作数,若该操作使得b到n的操作数增加,先操作b也能达到效果,但先操作a显然b位置将不可再操作,则总操作数将减小。
现在我们考虑如何按顺序求出原序列的删除位置。按照上面的贪心思路,用个线段树什么的就能求出,这里我用的是分块,效果差不多。
求出位置序列,假设为13 5 2 8 7 4 9 ,一个显然的结论是如果位置p被改变了的话,则在位置序列中p的后面的位置q,若 q > p q>p q>p则显然不会被操作,我们考虑前x个中在位置序列最前面的位置,假设x取6,那么最前面的就是5,根据上面的结论,5后面的操作都不能实现,若后面的操作q>5(前面的结论),q<5则被改变,所有对前缀我们维护下前面的最左边的位置即可。那么对后y个,我们直接算位置序列中前x的最左边位置的左边有多少个大于 n − y n-y n−y即可。离线bit维护下即可。
代码
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define pb push_back
#define lowbit(x) ((x)&(-x))
#define inf 0x3f3f3f3f
#define endl "\n"
#define MP(x,y) (make_pair(x,y))
#define pii pair<int,int>
#define pll pair<long long,long long>
using namespace std;
int n,q;
int a[300005];
int mn[300005];
int id[300005];
int tag[300005];
int sz;
int ans[300005],tot;
int c[300005];
int pre[300005];
int ps[300005],p[300005];
void add(int x){
if(x==0 || x>n) return;
for(;x<=n;x+=lowbit(x)) c[x]++;
}
int getsum(int x){
int res = 0;
if(x>n) x = n;
for(;x>0;x-=lowbit(x)) res+=c[x];
return res;
}
struct query{
int l,r,id;
bool operator < (const query b) const{
return r<b.r;
}
}ask[300005];
int main(){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
memset(ps,inf,sizeof(ps));
sz = sqrt(n);
int mx = n/sz;
memset(mn,0x3f,sizeof(mn));
for(int i=1;i<=n;i++){
id[i] = i/sz;
a[i] = i-a[i];
if(a[i]<0) a[i] = inf;
mn[id[i]] = min(mn[id[i]],a[i]);
}
while(1){
bool flag = 0;
int pos = -1;
for(int i = mx;i>=0;i--){
if(flag) break;
if(mn[i] -tag[i] <= 0){
mn[i] = inf;
for(int j = max(i*sz,1);j<min((i+1)*sz,n+1);j++){
a[j] -= tag[i];
if(a[j]<0) a[j] = inf;
if(a[j] == 0) pos = j;
}
if(pos!=-1){
flag = 1;
ans[tot++] = pos;
ps[pos] = tot;
a[pos] = inf;
for(int j = i+1;j<=mx;j++) tag[j]++;
for(int j=pos+1;j<min((i+1)*sz,n+1);j++){
a[j]--;
if(a[j]<0) a[j] = inf;
}
}
for(int j = i*sz;j<min((i+1)*sz,n+1);j++)
mn[i] = min(mn[i],a[j]);
tag[i] = 0;
}
}
if(!flag) break;
}
memset(p,0x3f,sizeof(p));
for(int i=1;i<=n;i++){
pre[i]=max(pre[i-1],tot-ps[i]+1);
p[i] = min(p[i-1],ps[i]);
if(p[i] == inf) p[i] = n+1;
}
for(int i=0;i<q;i++){
scanf("%d%d",&ask[i].l,&ask[i].r);
ask[i].id = i;
}
sort(ask,ask+q);
int now = n;
for(int i=0;i<q;i++){
while(now>=n-ask[i].r+1){
add(ps[now--]);
}
ans[ask[i].id] = tot-pre[ask[i].l]-getsum(p[ask[i].l]-1);
}
for(int i=0;i<q;i++){
printf("%d\n",ans[i]);
}
}
写的有点难看,主要是昨晚不太清醒的时候写的,当时有个问题始终没有解决,早上起来就会了,差点ak div2可惜了错过上大分的机会。