原题: https://cn.vjudge.net/problem/Gym-101864F
题意:
开始1到k为1,k+1到n为0,每次操作移动一个1到一个0的位置,保证操作合法。求每次操作后1到n有几个连续的0区间(100010有两个)
解析:
因为1e9的原因所以不能开数组。
考虑插入的情况:既然合法,也就是说当前位置之前为0,而且只会插在两个连续1区间的中间,所以对于连续1区间,我们不需要对整个区间的每个位置打标记,只需要对左端点和右端点打标记。那么在插入的时候,只需要判断当前空左边有没有打标记,右边有没有打标记即可。(两边有则空–,两边无则空++)
再考虑移走的情况,因为本就是01和10的关系,所以所有的操作都可以共享,即:对于一个连续0区间的左右端打标记,之后操作完全相同
#include<bits/stdc++.h>
using namespace std;
map<int,bool>non,vis;
int insert(int p){
int add=0;
int l=0,r=0;
if(vis[p+1])r++;
if(vis[p-1])l++;
if(l+r==2)add--;
else if(l+r==1);
else add++;
vis[p]=1;
if(l==1&&r==0){
non[p]=0;
non[p+1]=1;
}
else if(l==0&&r==1){
non[p]=0;
non[p-1]=1;
}
else if(l&&r){
non[p]=0;
}
else{
non[p]=0;
non[p+1]=non[p-1]=1;
}
return add;
}
int erase(int p){
int add=0;
int l=0,r=0;//左边没有
if(non[p+1])r++;
if(non[p-1])l++;
if(l+r==2)add--;
else if(l+r==1);
else add++;
non[p]=1;
if(l==1&&r==0){
vis[p]=0;
vis[p+1]=1;
}
else if(l==0&&r==1){
vis[p]=0;
vis[p-1]=1;
}
else if(l&&r){
vis[p]=0;
}
else{
vis[p]=0;
vis[p+1]=vis[p-1]=1;
}
return add;
}
int main(){
int t;scanf("%d",&t);int ca=0;
while(t--){
printf("Case %d:\n",++ca);
non.clear();vis.clear();
int n,k,q;
scanf("%d%d%d",&n,&k,&q);
vis[0]=vis[n+1]=vis[1]=vis[k]=1;
if(k<n)non[k+1]=non[n]=1;
int now=1;
while(q--){
int x,y;
scanf("%d%d",&x,&y);
if(x!=y)
now+=insert(y),
now+=erase(x);
printf("%d\n",now);
}
}
}