2019杭电多校第二场hdu6602 Longest Subarray(线段树)

Longest Subarray

题目传送门

解题思路

本题求一个最大的子区间,满足区间内的数字要么出现次数大于等于k次,要么没出现过。给定区间内的数字范围是1~c。

如果r为右边界,对于一种数字x,满足条件的左边界l的范围是r左边第一个x出现的位置+1(即这段区间内没有出现过x,如果x在1~r内都没有出现过,那么1~r自然都是l的合法范围),以及1到从右往左数数第k个x出现的位置(即这段区间内的x出现次数大于等于k)。所以我们只要找到同时是c种数字的合法左边界的位置中最小的,然后枚举所有的i作为右边界即可得出答案。

但是这样直接写肯定超时。所以我们用线段树来维护每个位置可以作为多少种数字的合法范围,以及一个区间内的最大值。在查询的时候只要尽量往左子树找就可以了。还有一个问题,难道我们要每次枚举都重新划分范围么?肯定是不行的。所以我们记录所有数字出现的位置,然后先让n为右边界,用线段树维护好其合法范围,再让右边界往左移,每次移动其实只是把旧的右边界归0,然后改变了旧的右边界上的数字的合法范围。我们已经记录了数字的出现位置,利用这个,只需要在线段树上进行几次修改即可,因为当左边界比右边界小的时候会得到负数,不会使答案更新,所以不归零也不会影响答案。

代码如下

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;

inline int read(){
    int res = 0, w = 0; char ch = 0;
    while(!isdigit(ch)){
        w |= ch == '-', ch = getchar();
    }
    while(isdigit(ch)){
        res = (res << 3) + (res << 1) + (ch ^ 48);
        ch = getchar();
    }
    return w ? -res : res;
}

const int N = 100005;

struct T{
    int l, r;
    int maxx, lazy;
}tree[N<<2];
int a[N];
vector<int> vec[N];

void build(int k, int l, int r)
{
    tree[k].l = l;
    tree[k].r = r;
    tree[k].lazy = tree[k].maxx = 0;
    if(l == r)
        return;
    int mid = (l + r) / 2;
    build(2*k, l, mid);
    build(2*k+1, mid + 1, r);
}

inline void push_down(int k)
{
    if(tree[k].lazy){
        tree[2*k].maxx += tree[k].lazy;
        tree[2*k+1].maxx += tree[k].lazy;
        tree[2*k].lazy += tree[k].lazy;
        tree[2*k+1].lazy += tree[k].lazy;   //+=
        tree[k].lazy = 0;
    }
}

void insert(int k, int l, int r, int u)
{
    if(l > r)
        return;
    if(tree[k].l >= l && tree[k].r <= r){
        tree[k].lazy += u;
        tree[k].maxx += u;
        return;
    }
    int mid = (tree[k].l + tree[k].r) / 2;
    push_down(k);
    if(l <= mid)
        insert(2*k, l, r, u);
    if(r > mid)
        insert(2*k+1, l, r, u);
    tree[k].maxx = max(tree[2*k].maxx, tree[2*k+1].maxx);
}

int n, c, k;

int query(int k)
{
    if(tree[k].maxx < c)
        return n + 1;
    if(tree[k].l == tree[k].r)
        return tree[k].l;
    push_down(k);
    if(tree[2*k].maxx == c)
        return query(2*k);
    else
        return query(2*k+1);
}

int main()
{
    while(scanf("%d%d%d", &n, &c, &k) != EOF){
        for(int i = 1; i <= n; i ++)
            a[i] = read();
        for(int i = 1; i <= n; i ++)
            vec[a[i]].push_back(i);
        build(1, 1, n);
        for(int i = 1; i <= c; i ++){
            if(vec[i].empty())
                insert(1, 1, n, 1);
            else {
                insert(1, vec[i][vec[i].size() - 1] + 1, n, 1);
                if(vec[i].size() >= k)
                    insert(1, 1, vec[i][vec[i].size() - k], 1);
            }
        }
        int ans = 0;
        for(int i = n; i >= 1; i --){
            ans = max(ans, i - query(1) + 1);
            if(vec[a[i]].size() >= k)
                insert(1, 1, vec[a[i]][vec[a[i]].size() - k], -1);
            vec[a[i]].pop_back();
            if(vec[a[i]].empty())
                insert(1, 1, i, 1);
            else {
                insert(1, vec[a[i]][vec[a[i]].size() - 1] + 1, i - 1, 1);
                if(vec[a[i]].size() >= k)
                    insert(1, 1, vec[a[i]][vec[a[i]].size() - k], 1);
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/whisperlzw/p/11274534.html

时间: 2024-08-01 20:41:55

2019杭电多校第二场hdu6602 Longest Subarray(线段树)的相关文章

2019 杭电多校 第二场

2019 Multi-University Training Contest 2 补题链接:2019 Multi-University Training Contest 2 1005 Everything Is Generated In Equal Probability (HDU-6595) 题意 给出一个整数 \(N\),在 \([1,N]\) 中随机生成一个 \(n\).然后生成长度为 \(n\) 的全排列 \([1, n]\). 对该排列运行一个程序,程序先求当前排列的逆序对对数,然后随

2019杭电多校第九场

2019杭电多校第九场 熟悉的后半场挂机节奏,又苟进首页了,很快乐 1001. Rikka with Quicksort upsolved 不是我做的,1e9调和级数分段打表 1002. Rikka with Cake solved at 01:11 有一个矩形,给你很多射线(射线只有横平竖直的四个方向),问把矩形切成了多少块 队友说答案是交点数加一,作为一个合格的工具人,当然是把队友的想法实现啦 二维坐标离散化枚举纵坐标维护横坐标,常规套路,树状数组也可以做(我是线段树写习惯了根本没想起来还有

2019年杭电多校第二场 1002题Beauty Of Unimodal Sequence(LIS+单调栈)

题目链接 传送门 思路 首先我们对\(a\)正反各跑一边\(LIS\),记录每个位置在前一半的\(LIS\)中应该放的位置\(ans1[i]\),后一半的位置\(ans2[i]\). 对于字典序最小的方案,我们找到第一个峰值,然后往前遍历.在\(i\)这个位置,如果它在\(LIS\)中放的位置是\(pos\),那么我们先看当前放在\(pos+1\)的值是否比它大,大的话就说明这个位置一定比前面放过在\(pos\)这个位置的更优(因为字典序更小,且\([1,i]\)一定可以放满\([1,pos-1

【2019.07.22】2019杭电多校第一场

补题地址:http://acm.hdu.edu.cn/listproblem.php?vol=56 题号:6578-6590 1001: 1002:线性基 https://blog.csdn.net/Cassie_zkq/article/details/96979461 1003: 1004: 1005:? 1006: 1007: 1008: 1009: 1010: 1011: 1012: 1013: 自闭场,但补题能学到好多算法和思维,继续加油鸭- 原文地址:https://www.cnblo

2019杭电多校第一场

所有题解都在注释里面. 1004: 1 /* 2 出题人的说法:最终通过停车线的时候,一定是一个车后面堵着剩余所有车.那么影响 3 时间的就只有最前面这辆车,所以对于每一辆车,假设是它与0车堵在一起的最靠前的一辆车. 4 那么可以计算出一个值.所有的车的计算值的最大值就是答案. 5 这个就是出题人的说法 ,时间复杂度是o(n) 6 7 我个人的理解:我们做这道题得注意到一个条件,就是对当前的车,先不要考虑后面的车. 8 只考虑它与前面的那一辆车的互相作用.考虑他们什么时候撞倒. 9 首先假设第一

Rikka with Game[技巧]----2019 杭电多校第九场:1005

Rikka with Game Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)Total Submission(s): 0    Accepted Submission(s): 0 Problem Description Though both Rikka and Yuta are busy with study, on their common leisure, they

2019 杭电多校 第一场

2019 Multi-University Training Contest 1 补题链接:2019 Multi-University Training Contest 1 1002 Operation (HDU-6579) 题意 给定包含 \(n\) 个数的序列,\(m\) 个询问.询问有两种操作,操作 \(0\) 表示在数组最后添加一个新元素,操作 \(1\) 表示查询区间 [l,r] 的子集的异或最大值. 询问强制在线. 题解 线性基 贪心 1004 Vacation (HDU-6581)

2019 杭电多校 第九场

2019 Multi-University Training Contest 9 补题链接:2019 Multi-University Training Contest 9 1005 Rikka with Game (HDU 6684) 题意 Rikka 和 Yuta 玩游戏.给定一个字符串.两人轮流对字符串操作.可以选择结束游戏,也可以改变其中一个字符,改变规则是:\(a\rightarrow b,b\rightarrow c,-,y\rightarrow z,z\rightarrow a.\

2015杭电多校第二场

Buildings Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 1622    Accepted Submission(s): 460 Problem Description Your current task is to make a ground plan for a residential building located