bzoj 4059: [Cerc2012]Non-boring sequences 线段树+扫描线

题意

我们害怕把这道题题面搞得太无聊了,所以我们决定让这题超短。一个序列被称为是不无聊的,仅当它的每个连续子序列存在一个独一无二的数字,即每个子序列里至少存在一个数字只出现一次。给定一个整数序列,请你判断它是不是不无聊的。
1 <= n <= 200000

分析

首先求出ls[x]和nx[x]表示上一个和x相同的位置和下一个与x相同的位置。
然后显然x的贡献就是(ls[x],x],[x,nx[x])
意思是左端点在(ls[x],x]右端点再[x,nx[x])的区间是有趣的。
那我们可以构建一个平面直角坐标系,一个点表示一个区间,其横坐标表示右端点,纵坐标表示左端点,那么我们就可以把上述贡献看做是一个矩形。
显然一个横坐标x满足题目条件的话则必有横坐标为x纵坐标在[1,x]内的点被矩形覆盖掉。
那么就可以用线段树+扫描线来搞了。

做完之后发现原来正解并不是这个。。。

恩刚写完的时候交了一发,发现T了,然后就开始了一段惨无人道的卡常之旅。
作为一个上过松爷卡常专题的人,对于卡常我还是略懂12的,不就是把位运算啊读入优化输出优化三元运算符之类的鬼东西通通加上么。
加上之后就勉强过掉了,bzoj上时限10s我跑了11s+。。。在AC名单上面居然还看不到我2333

代码

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

const int N=200005;

int n,a[N],b[N],nx[N],ls[N],w[N];
struct tree{int tag,s;}t[N*10];
struct data{int x,x1,y1,op;}q[N*2];

int read()
{
    int x=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

void build(int d,int l,int r)
{
    t[d].tag=t[d].s=0;
    if (l==r) return;
    int mid=(l+r)>>1;
    build(d<<1,l,mid);build(d<<1|1,mid+1,r);
}

void updata(int d,int l,int r)
{
    if (t[d].tag) t[d].s=r-l+1;
    else t[d].s=t[d<<1].s+t[d<<1|1].s;
}

void ins(int d,int l,int r,int x,int y,int z)
{
    if (x>y) return;
    if (l==x&&r==y)
    {
        if (z==1) t[d].tag++,t[d].s=r-l+1;
        else t[d].tag--;
        updata(d,l,r);
        return;
    }
    int mid=(l+r)>>1;
    ins(d<<1,l,mid,x,min(y,mid),z);
    ins(d<<1|1,mid+1,r,max(x,mid+1),y,z);
    updata(d,l,r);
}

int query(int d,int l,int r,int x,int y)
{
    if (x>y) return 0;
    if (t[d].tag) return y-x+1;
    if (l==x&&r==y) return t[d].s;
    int mid=(l+r)>>1;
    return query(d<<1,l,mid,x,min(mid,y))+query(d<<1|1,mid+1,r,max(x,mid+1),y);
}

bool cmp(data a,data b)
{
    return a.x<b.x;
}

bool solve()
{
    sort(b+1,b+n+1);
    int b1=unique(b+1,b+n+1)-b-1;
    for (int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+b1+1,a[i])-b;
    for (int i=1;i<=n;i++) ls[i]=w[a[i]],w[a[i]]=i;
    for (int i=1;i<=n;i++) w[a[i]]=0;
    for (int i=n;i>=1;i--) nx[i]=w[a[i]]?w[a[i]]:n+2,w[a[i]]=i;
    for (int i=1;i<=n;i++) w[a[i]]=0;
    build(1,1,n);
    int q1=0;
    for (int i=1;i<=n;i++)
    {
        q[++q1].x=i;q[q1].x1=ls[i]+1;q[q1].y1=i;q[q1].op=1;
        if (nx[i]==n+2) continue;
        q[++q1].x=nx[i]-1;q[q1].x1=ls[i]+1;q[q1].y1=i;q[q1].op=-1;
    }
    sort(q+1,q+q1+1,cmp);
    for (int i=1,p=1;i<=n;i++)
    {
        for (;p<=q1&&q[p].x==i;p++) ins(1,1,n,q[p].x1,q[p].y1,q[p].op);
        if (p>q1)
        {
            if (t[1].s==n) return 1;
            else return 0;
        }
        if (query(1,1,n,1,i)<i) return 0;
    }
    return 1;
}

int main()
{
    int T=read();
    while (T--)
    {
        n=read();
        for (int i=1;i<=n;i++) b[i]=a[i]=read();
        if (solve()) puts("non-boring");
        else puts("boring");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值