2021暑假 HDU中超 第五场 1009

被这个题折磨了好几天
从比赛中的奇怪思路绕到洛谷原题,从分治绕到树状数组,还先去搞了半天模板题,可谓是曲线救国

2021暑假 HDU中超 第五场 1009

Array

Time Limit: 15000/8000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 235 Accepted Submission(s): 48

题意

给你一个序列\(a[1...n]\)​,求存在绝对众数的子区间个数。

绝对众数指:区间中出现次数最多的那个数,出现次数严格大于区间长度的一半。

Sample Input
1
10
3303 70463 3303 3303 3303 70463 3303 3303 70463 70463
Sample Output
47
考场思路:

玄学而虚假的主席树乱搞做法

曲线救国:

首先注意到这是洛谷原题(RNM退钱)

分治做法:(只能过洛谷过不了比赛题)

对于区间 \([l,r]\)​,中间值 \(mid\)​, 我们需要统计经过 \(mid\)​ 的所有合法区间 \([l', r']\)

而如果 \([l',r']\)​​ 拥有绝对众数,那么 \([l', mid]\)\([mid+1, r']\) 中至少有一个有绝对众数

那么可以从 \(mid\) 往两边搜,统计每种数出现的次数,如果搜到某个位置时,当前数出现次数大于区间长度的一半,则这个数有可能成为绝对众数​。我们首先 \(O(r-l)\) 地找到所有可能的结果

再依次枚举这些众数,对于众数 \(X\)​​,我们可以按投票的思想,把所有的 \(X\)​​​ 记为1,其他数记为-1,计算前缀和,这样可以判断任意区间是否满足条件。

若区间 \([l',r']\) 满足 \(sum[r'] - sum[l'-1] > 0\),则这个区间有绝对众数 \(X\)

也就是说需要在 \(mid\)​ 左右找到满足 \(sum[r'] > sum[l'-1]\)​ 的点对个数

至此问题转化为了一个比较经典的模型,用树状数组搞即可。

代码复杂度 \(O(nlog^3n)\) 洛谷能过

但这题直接从1e5加强到1e6,直接T飞

AC做法:

首先记录每一种数字出现的位置,然后枚举每一种数字,设当前枚举到 \(X\)

然后还是按投票的思想,把所有的 \(X\)​​ 记为1,其他数记为-1,计算前缀和 \(sum\)

若区间 \([l',r']\) 满足 \(sum[r'] - sum[l'-1] > 0\),则这个区间有绝对众数 \(X\)(和上面做法一样)

考虑从左到右遍历,对于当前位置的前缀和 \(sum\)​​ ,我们需要求出它左边比它小的前缀和个数

记录每个前缀和出现的次数为 \(cnt[x]\),用树状数组维护​和查询

复杂度大概是 \(O(n^2logn)\)

想到我们记录了每个数字出现的位置,那么是不是可以在遍历的时候直接跳过一串连续的-1?

跳的时候面临两个问题:

  1. 如何把这一串-1位置上的前缀和计入 \(cnt\) 数组
  2. 如何计算这些-1位置对答案的贡献

对于问题二:

​ 首先回忆一下:位置 \(p\)​​​​​​ 的贡献,就是 \(p\)​ 前面比 \(sum[p]\)​​​​​ 小的前缀和个数

​ 也就是 \(cnt\)​​ 数组从最小值加到 \(sum[p]\)​​​ 为止的前缀和,设这个前缀和为 \(ssum\)

\(ssum[p]\)​ 是位置 \(p\)​ 上的答案

​ 而我们需要知道这一连串-1的答案之和

​ 显然再搞一遍前缀和就行了。。。设 \(sssum\) 是这个东西。。。

现在问题被转化成了:我们要维护 \(cnt\)​ 数组的前缀和的前缀和

别急 还没完

对于问题一,其实很简单,对 \(cnt\) 数组搞个差分数组 \(d\)​ 就好了,做到 \(O(1)\) 插入

那么只需要维护 \(d\)​​​​ 数组的前缀和的前缀和的前缀和。。。

其实写起来没那么麻烦

我们只需要一个支持单点修改,三阶前缀和查询的数据结构

还是树状数组!

具体树状数组的高阶前缀和搞法

打包起来会比较好写一点

struct BitTree{//4行普通树状数组
    ll t[2*maxn];
    int lb(int x) {return x&-x;}
    void add(int p, ll ad){for(int i=p;i<=2*n+20;i+=lb(i)) t[i]+=ad;}
    ll sum(int p){ return p?t[p] + sum(p-lb(p)):0ll;}
};

struct BitTree3{//三阶前缀和的树状数组
    BitTree bt1, bt2, bt3;
    void add(int p, ll ad){
        bt1.add(p, ad);
        bt2.add(p, ad * p);
        bt3.add(p, ad * p * p);
    }
    ll sum(ll p){ return ((p+1)*(p+2) * bt1.sum(p) - (2*p+3) * bt2.sum(p) + bt3.sum(p)) / 2;}
    void modify(int l, int r, int dt = 1){
        add(l + bs, dt);
        add(r + 1 + bs, -dt);
    }
    ll query(int l, int r){
        if(l > r) return 0;
        return sum(r - 1 + bs) - sum(l - 2 + bs);
    }
};

AC代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#ifdef ACM_LOCAL
const int maxn = 1000040;
#else
const int maxn = 1000040;
#endif

int n; ll bs;
vector<int> pos[maxn];

struct BitTree{//4行树状数组
    ll t[2*maxn];
    int lb(int x) {return x&-x;}
    void add(int p, ll ad){for(int i=p;i<=2*n+20;i+=lb(i)) t[i]+=ad;}
    ll sum(int p){ return p?t[p] + sum(p-lb(p)):0ll;}
};

struct BitTree3{//三阶前缀和的树状数组
    BitTree bt1, bt2, bt3;
    void add(int p, ll ad){
        bt1.add(p, ad);
        bt2.add(p, ad * p);
        bt3.add(p, ad * p * p);
    }
    ll sum(ll p){ return ((p+1)*(p+2) * bt1.sum(p) - (2*p+3) * bt2.sum(p) + bt3.sum(p)) / 2;}
    void modify(int l, int r, int dt = 1){
        add(l + bs, dt);
        add(r + 1 + bs, -dt);
    }
    ll query(int l, int r){
        if(l > r) return 0;
        return sum(r - 1 + bs) - sum(l - 2 + bs);
    }
};

BitTree3 t;
void solve(){
    cin >> n >> bs;
    for(int i=1;i<=n;i++){
        int xx;
        cin >> xx;
        pos[xx].push_back(i);
    }
    ll ans = 0;
    bs = n + 10;
    for(int i=0;i<=1e6;i++){
        if(!pos[i].size()) continue;
        ll sum = 2 - pos[i][0];
        t.modify(1-pos[i][0], 0);
        t.modify(sum, sum);
        ans++;

        for(int j=1;j<pos[i].size();j++){
            int l = pos[i][j] - pos[i][j-1] - 1;
            ans += t.query(sum - l, sum - 1);
            t.modify(sum - l, sum - 1);
            (sum -= l) += 1;
            ans += t.query(sum, sum);
            t.modify(sum, sum);
        }
        int l = n + 1 - pos[i][pos[i].size()-1] - 1;
        ans += t.query(sum - l, sum - 1);

        //相同操作撤销
        t.modify(1-pos[i][0], 0, -1);
        sum = 2 - pos[i][0];
        t.modify(sum, sum, -1);
        for(int j=1;j<pos[i].size();j++){
            int l = pos[i][j] - pos[i][j-1] - 1;
            t.modify(sum - l, sum - 1, -1);
            (sum -= l) += 1;
            t.modify(sum, sum, -1);
        }

        pos[i].clear();
    }
    cout << ans << '\n';
}
posted @ 2021-08-04 20:07  lx_tyin  阅读(170)  评论(6编辑  收藏  举报