雅礼集训 Day7 T1 Equation 解题报告

Reverse

题目背景

\(\text{G}\)有一个长度为\(n\)\(01\)\(T\),其中只有\(T_S=1\),其余位置都是\(0\)。现在小\(\text{G}\)可以进行若干次以下操作:

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

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

输入输出格式

输入格式

从文件reverse.in中读入数据.

第一行四个整数\(n,K,m,S\).

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

输出格式

输出到文件reverse.out中.

输出一行\(n\)个整数,对于第\(i\)个整数,如果可以通过若干次操作使得\(T_i=1\),输出最小操作次数,否则输出\(-1\).

说明

对于所有数据,有\(1≤n≤10^5,1≤S,k≤n,0≤m≤n\).

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

  • \(\text{Subtask1}(24\%), n≤10\).

  • \(\text{Subtask2}(22\%), n≤10^3\).

  • \(\text{Subtask3}(3\%), k=1\).

  • \(\text{Subtask4}(8\%), k=2\).

  • \(\text{Subtask5}(43\%)\), 没有特殊的约束。


题目其实并不难

发现可以连边直接bfs,可以拿到57pts的暴力分

发现边的数量很多,需要支持动态删点

用两颗平衡树分别维护位置为奇数时和位置为偶数时

然后每次找到可翻转的左边和右边,在平衡上二分,bfs然后删掉点就可以了

每个点只会被删掉一次,复制度差不多是\(O(nlogn)\)


Code:

#include <cstdio>
#include <cstring>
#include <set>
const int N=1e5+10;
std::set <int> s1,s2;
std::set <int>::iterator it;
int n,k,m,s,l=1,r,q[N<<2],used[N],ans[N],lp,rp,d;
int min(int x,int y){return x<y?x:y;}
int max(int x,int y){return x>y?x:y;}
int main()
{
    scanf("%d%d%d%d",&n,&k,&m,&s);
    memset(ans,-1,sizeof(ans));
    used[s]=1,q[++r]=s,ans[s]=0;
    for(int p,i=1;i<=m;i++) scanf("%d",&p),used[p]=1;
    for(int i=1;i<=n;i+=2)
    {
        if(!used[i]) s1.insert(i);
        if(!used[i+1]) s2.insert(i+1);
    }
    while(l<=r)
    {
        int p=q[l++];
        lp=max(p-k+1,max(k-p+1,1)),rp=min(p+k-1,min(2*n+1-k-p,n));
        if(p-k&1)//s2
        {
            it=s2.lower_bound(lp);
            while(it!=s2.end()&&(d=*it)<=rp)
            {
                q[++r]=d;
                ans[d]=ans[p]+1;
                it++;
                s2.erase(d);
            }
        }
        else
        {
            it=s1.lower_bound(lp);
            while(it!=s1.end()&&(d=*it)<=rp)
            {
                q[++r]=d;
                ans[d]=ans[p]+1;
                it++;
                s1.erase(d);
            }
        }
    }
    for(int i=1;i<=n;i++)
        printf("%d ",ans[i]);
    return 0;
}

2018.10.7

转载于:https://www.cnblogs.com/butterflydew/p/9750752.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值