2019HDU多校训练第二场 Longest Subarray 推导+线段树

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6602
题目大意:给你一个串,问满足以下条件的子串中最长的是多长:对于每个数字,要么在这个子串没出现过,要么出现次数超过k次。

转:
思路:对于这类问题,常常转化为数据结构的询问问题。我们考虑枚举右端点,对于当前右端点,我们单独考虑每一种数的合法区间。假设当前枚举的右端点是i,考虑的数字是c,在右端点左边离i最近的数字c的位置是p1,离i第k远的数字c的位置是p2, 容易发现,数字c的合法区间为[1, p2]和[p1 + 1, i],对应的情况是选择这个数至少k个和不选这个数。那么,如果我们用线段树来维护覆盖的区间,对于每一种数的合法区间在线段树上+1,这样我们只要找到在i前面值为c的最小的位置就是右端点为i的最优解。由于每次右端点只移动1,所以可以在O(logn)时间内维护一个数合法区间的变化。最小位置的找法可以通过维护区间最大值然后在线段树上二分即可。
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;

struct Node
{
    int L, R;
}node[405000];

int mx[400050], add[400050];

void BT(int i, int L, int R)
{
    node[i].L=L, node[i].R=R;
    mx[i]=0, add[i]=0;
    if(L==R)
    {
        return ;
    }
    BT((i<<1), L, (L+R)/2);
    BT((i<<1)+1, (L+R)/2+1, R);
}

void UPADD(int i)
{
    if(add[i])
    {
        add[i<<1]+=add[i];
        add[(i<<1)+1]+=add[i];

        mx[i<<1]+=add[i];
        mx[(i<<1)+1]+=add[i];

        add[i]=0;
    }
}

void UP(int i, int L, int R, int c)
{
    if(node[i].L==L&&node[i].R==R)
    {
        add[i]+=c;
        mx[i]+=c;

        return ;
    }

    if(node[i].L==node[i].R)
    {
        return ;
    }

    UPADD(i);
    int mid=(node[i].L+node[i].R)/2;
    if(R<=mid)
    {
        UP(i<<1, L, R, c);
    }
    else if(L>mid)
    {
        UP((i<<1)+1, L, R, c);
    }
    else
    {
        UP(i<<1, L, mid, c);
        UP((i<<1)+1, mid+1, R, c);
    }

    mx[i]=max(mx[i<<1],mx[(i<<1)+1]);
}

int cx(int i, int L, int R, int k)
{
    if(L==R)
    {
        return L;
    }
    UPADD(i);
    int mid=(L+R)/2;

    if(mx[i<<1]==k)
    {
        return cx(i<<1, L, mid, k);
    }
    if(mx[(i<<1)+1]==k)
    {
        return cx((i<<1)+1, mid+1, R, k);
    }

    return -1;
}

int a[100050];
int main()
{
    int n, c, k;
    while(~scanf("%d%d%d", &n, &c, &k))
    {
        vector<int> pos[100050];
        for(int i=1;i<=c;i++)
        {
            pos[i].push_back(0);
        }
        for(int i=1;i<=n;i++)
        {
            scanf("%d", &a[i]);
            pos[a[i]].push_back(i);
        }
        if(k==1)
        {
            printf("%d\n", n);
            continue;
        }
        BT(1, 1, n);
        int ans=0, cur[100050]={0};
        for(int i=1;i<=n;i++)
        {
            int x=a[i];
            int p=++cur[x];

            UP(1, i, i, c-1);

            if(pos[x][p-1]+1<=pos[x][p]-1)
            {
                UP(1, pos[x][p-1]+1, pos[x][p]-1, -1);
            }
            if(p>=k)
            {
                UP(1, pos[x][p-k]+1, pos[x][p-k+1], 1);
            }

            int temp=cx(1, 1, n, c);

            if(temp!=-1)
            {
                ans=max(ans, i-temp+1);
            }
        }
        printf("%d\n", ans);
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值