2018.11.07-4031-reverse

题目描述:

小G有一个长度为n的01串T,其中只有TS = 1,其余位置都是0。现在小G可以进行若干以下操作:

选择一个长度为K的连续子串(K是给定的常数),翻转这个子串。

对于每个i,i∈[1,n]i,i∈[1,n],小G想知道最少要进行多少操作使得Ti = 1。特别的,有m个“禁止位置”,你需要保证在操作过程中1始终不在任何一个禁止位置上。

输入:

一行四个整数n,K,m,S。

接下来一行m个整数表示禁止位置。

输入:

输出一行n个整数,对于 i个整数,如果可以通过若干操作使得Ti=1,输出最小操作次数,否则输出-1。​​​​​​​

数据范围:

对于所有数据,有1≤n≤105,1≤S,k≤n,0≤m≤n。

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

Subtask1(24%), n≤10。

Subtask2(22%), n≤103。

Subtask3(3%), k=1。

Subtask4(8%), k=2。

Subtask5(43%), 没有特殊的约束。

算法标签:stl的使用,貌似并差集也可写吧

思路:

对于每个位置,可以更新的位置是一个区间内的奇数或偶数,所以用两个set存下分开存下奇数和偶数,每次把已访问的点出set,所以每个位置只会访问到一次。

以下代码:
#include<bits/stdc++.h>
#define il inline
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=1e5+5;
int n,k,s,m,d[N];bool f[N];
set<int> t1,t2;queue<int> q;
set<int>::iterator it1,it2,it;
il int read(){int x,f=1;char ch;_(!)ch=='-'?f=-1:f;x=ch-48;_()x=(x<<1)+(x<<3)+(ch^48);return f*x;}
int main()
{
    n=read();k=read();m=read();s=read();
    for(int i=1;i<=m;i++)f[read()]=1;
    for(int i=1;i<=n;i+=2)if(!f[i]&&i!=s)t1.insert(i);
    for(int i=2;i<=n;i+=2)if(!f[i]&&i!=s)t2.insert(i);
    q.push(s);for(int i=1;i<=n;i++)d[i]=-1;d[s]=0;
    if(k&1){
        while(!q.empty()){
            int x=q.front();q.pop();
            int l=max(x-k+1,1),r=min(n,x+k-1);
            l+=l-x+k-1;r+=r-x-k+1;
            if(x&1){
                it1=t1.lower_bound(l);
                it2=t1.upper_bound(r);
                for(it=it1;it!=it2&&it!=t1.end()&&*it<=n;){
                    d[*it]=d[x]+1;q.push(*it);t1.erase(it++);
                }
            }
            else{
                it1=t2.lower_bound(l);
                it2=t2.upper_bound(r);
                for(it=it1;it!=it2&&it!=t2.end()&&*it<=n;){
                    d[*it]=d[x]+1;q.push(*it);t2.erase(it++);
                }
            }
        }
    }
    else{
        while(!q.empty()){
            int x=q.front();q.pop();
            int l=max(x-k+1,1),r=min(n,x+k-1);
            l+=l-x+k-1;r+=r-x-k+1;
            if(x&1){
                it1=t2.lower_bound(l);
                it2=t2.upper_bound(r);
                for(it=it1;it!=it2&&it!=t2.end()&&*it<=r;){
                    d[*it]=d[x]+1;q.push(*it);t2.erase(it++);
                }
            }
            else{
                it1=t1.lower_bound(l);
                it2=t1.upper_bound(r);
                for(it=it1;it!=it2&&it!=t1.end()&&*it<=r;){
                    d[*it]=d[x]+1;q.push(*it);t1.erase(it++);
                }
            }
        }
    }
    for(int i=1;i<n;i++)printf("%d ",d[i]);printf("%d",d[n]);
  return 0;
}
View Code

我的沙雕分讨

转载于:https://www.cnblogs.com/Jessie-/p/9925741.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值