luogu2253 好一个一中腰鼓!

先说一个小trick,一开始我们把他赋值成是红、白相间的,查询就查询的是全红或全白即可。

然后就可以做啦

题解里面好像都是线段树

暴力的题解好像都被del了

貌似暴力交上去也过不了了

然后我想说

分块大法好!

把同学们他分成\(\lfloor\sqrt N\rfloor\)

每块维护八个信息:

左边连续白长度,右边连续白长度,块内连续白长度,是否全白,

左边连续红长度,右边连续红长度,块内连续红长度,是否全红,

记录两个不变的信息:

id(废话),块的长度

每次更新,找到那个同学在哪个块,整块全部暴力更新,根号N
怎么更新,整个快全扫一遍,统计白的数量和红的数量能把是否全白全红做了,中途统计当前连续白红数量能把最长块内连续长度做了
然后从块左端往右扫,右端往左扫,把左右两端的长度高了

每次查询,也是\(\sqrt N\),乱搞搞
怎么搞,先枚举两种颜色,然后枚举所有区块,更新一个当前能延伸的长度,如果这个区块全是一种颜色就+=(可以连上),否则就直接等于他右端的长度,具体看代码

复杂度大概\(O(M\sqrt N)\)

由于我经常用调试输出,所以我就没有删除调试输出

#include <cstdio>
#include <iostream>
#include <cmath>

using namespace std;

int n, m, size;
int a[20010], id[20010];
int len[200], l[200], r[200];
struct info
{
    bool all;
    int l, r, x;
}b[200][2];

int query();

void print()
{
    printf("输出调试信息\n");
    for (int i = 1; i <= n; i++)
        printf("%d%c", a[i], i == n ? '\n' : ' '); 
    for (int i = 1; i <= id[n]; i++)
    {
        printf("第%d块 (%d, %d)", i, l[i], r[i]); 
        for(int t = 0; t <= 1; t++)
            printf("[%d %d %d %d]%c", b[i][t].all, b[i][t].l, b[i][t].x, b[i][t].r, t?'\n':' '); 
    }
    printf("当前答案%d\n", query()); 
}

//更新区块x 
void update(int x)
{
//  printf("更新了区块%d\n", x); 
    int t[2] = {0, 0}, f[2] = {0, 0};
    b[x][0].x = b[x][1].x = 0;
    for (int i = l[x]; i <= r[x]; i++)
    {
        t[a[i]]++;
        f[a[i]]++;
        if(a[i] != a[i - 1])
        {
            b[x][a[i - 1]].x = max(b[x][a[i - 1]].x, f[a[i - 1]]);
            f[a[i - 1]] = 0;
        }
    }
    b[x][a[r[x]]].x = max(b[x][a[n]].x, f[a[r[x]]]);
    b[x][0].all = (t[0] == len[x]);
    b[x][1].all = (t[1] == len[x]);
    b[x][a[l[x]]].l = 1;
    b[x][a[l[x]] ^ 1].l = 0;
    b[x][a[r[x]]].r = 1;
    b[x][a[r[x]] ^ 1].r = 0;
    for (int i = l[x] + 1; a[i] == a[i - 1] && i <= r[x]; i++)
        b[x][a[l[x]]].l++;
    for (int i = r[x] - 1; a[i] == a[i + 1] && i >= l[x]; i--)
        b[x][a[r[x]]].r++;
}

//查询当前状态的最大值
int query()
{
    int maxlength[2] = {0, 0}, nowlength[2] = {0, 0}; 
    for (int t = 0; t <= 1; t++)
    {
        for (int i = 1; i <= id[n]; i++)//枚举所有区块 
        {
            maxlength[t] = max(maxlength[t], b[i][t].l + nowlength[t]);
            maxlength[t] = max(maxlength[t], b[i][t].x);
            if(b[i][t].all == 1)
                nowlength[t] += len[i];
            else
                nowlength[t] = b[i][t].r;
        }
        maxlength[t] = max(maxlength[t], nowlength[t]);
    }
    return max(maxlength[0], maxlength[1]);
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        a[i] = (i & 1);
    size = sqrt(n);
    for (int i = 1; i <= n; i++)
    {
        id[i] = (i - 1) / size + 1;
        len[id[i]]++;
        if ((i - 1) % size == 0)
            l[id[i]] = i;
        if (i % size == 0)
            r[id[i]] = i;
    }
    r[id[n]] = n;
    for (int i = 1; i <= id[n]; i++)
        update(i);
//  print();
    for (int x, i = 1; i <= m; i++)
    {
        scanf("%d", &x);
        a[x] ^= 1;
        update(id[x]);
//      print();
        printf("%d\n", query());
    }
    return 0;
}

瞎扯几句

据说写分快是对出题人的不尊重??但是这题难度标签对我不尊重2333。。。

一个线段树模板就是绿题了,这题黄题 显然不用线段树能过,所以我就写了分块

经验/启示:以后懒得写线段树但是又比较勤快可以试试分块

转载于:https://www.cnblogs.com/oier/p/9596652.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值