题目描述
小G有一个长度为n的01串T,其中只有TS = 1,其余位置都是0。现在小G可以进行若干以下操作:
选择一个长度为K的连续子串(K是给定的常数),翻转这个子串。
对于每个i,i∈[1,n],小G想知道最少要进行多少操作使得Ti = 1。特别的,有m个“禁止位置”,你需要保证在操作过程中1始终不在任何一个禁止位置上。
题解
其实我们很容易发现一个 n k nk nk的算法,但是转移的时候全部扫一遍太慢了,于是有一种猥琐的方法,开两个set,维护下标,一开始先选好一个区间,在区间内lower_bound然后迭代器++
代码
#include <bits/stdc++.h>
#define maxn 1000005
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
int read(){
int res; bool f=1; char c;
while(!isdigit(c=getchar())) if(c=='-') f=0; res=(c^48);
while(isdigit(c=getchar())) res=(res<<3)+(res<<1)+(c^48);
return f?res:-res;
}
int n,k,m,s;
queue<int> Q;
set<int>S[2];
set<int>::iterator it;
bool vis[maxn];
int dis[maxn];
int main(){
memset(dis,-1,sizeof dis);
n=read(); k=read(); m=read(); s=read();
for(int i=1;i<=m;i++) vis[read()]=1;
for(int i=1;i<=n;i++){
if(!vis[i] && i^s) S[i&1].insert(i);
}
Q.push(s); dis[s]=0;
while(!Q.empty()){
int x=Q.front(),l=max(1,x-k+1),r=min(n,x+k-1); Q.pop(); int ty=(x&1)^(k&1)^1;
l+=l-(x-k+1); r+=r-(x+k-1); it=S[ty].lower_bound(l);
while(it!=S[ty].end() && (*it)<=r){
dis[*it]=dis[x]+1; Q.push(*it);
S[ty].erase(it++);
}
}
for(int i=1;i<=n;i++) printf("%d ",dis[i]);
return 0;
}