NOIP 模拟 1005

reverse

给出一个长度为n的01序列,只有s位置为1,其余为0.

可以通过选择一个长度为k的序列进行翻转,对于每个位置求出至少多少次能够成为1.

有m个位置在任何时候都不能为1。

对于所有数据,有1 <=n <=105; 1 <= S; k <= n; 0 <= m <= n.

保证S不是禁止位置,但禁止位置可能有重复。

题解

一开始看成区间取反了。。。

可以看出就是最短路,而且边权为1,所以可以bfs求。

但是不可能每次都把区间枚举出来,所以考虑优化。

对于左端点在i的区间,x能翻到的位置是2*i+k-x-1

所以发现能到的位置奇偶性相同且连续,所以想到原来用到的一种方法:并查集

将用过的位置y连向y+2,下次就不会找到他。

边界是真的难受

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int maxn=100005;
int n,m,k,s;
int fa[maxn],dis[maxn];
bool no[maxn];

template<class T>inline void read(T &x){
    x=0;int f=0;char ch=getchar();
    while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x = f ? -x : x ;
}

int find(int x){return fa[x]==x ? x : fa[x]=find(fa[x]);}

void bfs(){
    queue<int> q;
    memset(dis,-1,sizeof(dis));
    dis[s]=0;
    q.push(s);
    while(!q.empty()){
        int x=q.front();
        q.pop();
        int y,r;
        if(x-k+1<0) y=find(k+1-x);
        else y=find(x-k+1);
        if(x+k-1>n) r=2*n-k-x+1;
        else r=x+k-1;
        for(;y<=r;y=find(y+2)){
          if(dis[y]==-1&&!no[y]){//no不判好像也行 
              dis[y]=dis[x]+1;
              q.push(y);
              fa[y]=find(y+2);
            }
        }
    }
}

int main(){
    freopen("reverse.in","r",stdin);
    freopen("reverse.out","w",stdout);
    read(n);read(k);read(m);read(s);
    for(int i=1;i<=n+2;i++) fa[i]=i;
    for(int i=1;i<=m;i++){
        int x;read(x);
        if(!no[x]) no[x]=true,fa[x]=find(x+2);
    }
    bfs();
    for(int i=1;i<=n;i++) printf("%d ",dis[i]);
}
reverse

还有线段树建边或者set维护也行。


Silhouette


 

seat

猜你喜欢

转载自www.cnblogs.com/sto324/p/11625944.html