Educational Codeforces Round 95 (Rated for Div. 2) G. Three Occurrences

题目链接

一、题意

给你n个数的序列a,问你区间内每个数的出现次数是3的区间个数。

数据范围:1 \leqslant n \leqslant 5 \cdot 10^5,1\leqslant a_i \leqslant n

二、题解

学习的是tourist的代码。

大致做法是从右到左枚举左端点,然后区间查询合法右端点的个数。

右端点的值是0表示合法,1表示不合法,那么区间询问最小值和最小值的个数,如果最小值是0,答案累加最小值的个数即可。

具体说下怎么维护区间右端点。

设区间左端点是l,区间右端点是r

从右到左枚举l,只会影响和a[l]有关的区间。对答案有贡献的是a[l]个数是3的区间。

具体细节看下图和代码。

 

 

三、代码

#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define sz(x)  (int)x.size()
#define cl(x)  x.clear()
#define all(x)  x.begin() , x.end()
#define rep(i , x , n)  for(int i = x ; i <= n ; i ++)
#define per(i , n , x)  for(int i = n ; i >= x ; i --)
#define mem0(x)  memset(x , 0 , sizeof(x))
#define mem_1(x)  memset(x , -1 , sizeof(x))
#define mem_inf(x)  memset(x , 0x3f , sizeof(x))
#define debug(x)  cerr << #x << " = " << x << '\n'
#define ddebug(x , y)  cerr << #x << " = " << x << "   " << #y << " = " << y << '\n'
#define ios std::ios::sync_with_stdio(false) , cin.tie(0)
using namespace std ;
typedef long long ll ;
typedef long double ld ;
typedef pair<int , int> pii ;
typedef pair<ll , ll> pll ;
typedef double db ;
const int mod = 998244353 ;
const int maxn = 5e5 + 10 ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ; 
int n , a[maxn] ;
struct seg_tree
{
    ll mn[maxn << 2] , lazy[maxn << 2] , cnt[maxn << 2] ;  
    int ls(int x){ return x << 1 ; }
    int rs(int x){ return x << 1 | 1 ; }
    void push_up(int p)
    { 
        mn[p] = min(mn[ls(p)] , mn[rs(p)]) ;
        cnt[p] = 0 ;
        if(mn[ls(p)] == mn[p])  cnt[p] += cnt[ls(p)] ;
        if(mn[rs(p)] == mn[p])  cnt[p] += cnt[rs(p)] ;
    }
    void build(int id , int l , int r)
    {
        lazy[id] = 0 ;
        if(l == r) {mn[id] = 0 , cnt[id] = 1 ; return ;}
        int mid = (l + r) >> 1 ;
        build(ls(id) , l , mid) ;
        build(rs(id) , mid + 1 , r) ;
        push_up(id) ;
    } 
    void f(int id , int l , int r , ll k)
    {
        lazy[id] += k ;
        mn[id] += k ;
    }
    void push_down(int id , int l , int r)
    {
        int mid = (l + r) >> 1 ;
        f(ls(id) , l , mid , lazy[id]) ;
        f(rs(id) , mid + 1 , r , lazy[id]) ;
        lazy[id] = 0 ;
    }
    void add(int id , int l , int r , int x , int y , int k)
    {
        if(x > y || x > r || y < l)  return ;
        if(x <= l && r <= y)
        {
            mn[id] += k ;
            lazy[id] += k ;
            return ;
        }
        push_down(id , l , r) ;
        int mid = (l + r) >> 1 ;
        if(x <= mid)  add(ls(id) , l , mid , x , y , k) ;
        if(y > mid)   add(rs(id) , mid + 1 , r , x , y , k) ;
        push_up(id) ;
    }
    pll query(int id , int l , int r , int x , int y)  //返回区间最小值和最小值的个数
    {
        if(x > y || x > r || y < l)  return {1e18 , 0} ;
        pll ans = {1e18 , 0} ;
        if(x <= l && r <= y)  return {mn[id] , cnt[id]} ; 
        int mid = (l + r) >> 1 ;
        push_down(id , l , r) ;
        if(x <= mid) 
        {
            pll res = query(ls(id) , l , mid , x , y) ;
            if(ans.fi > res.fi)  ans = res ;
            else if(ans.fi == res.fi)  ans.se += res.se ;
        }
        if(y > mid)  
        {
            pll res = query(rs(id) , mid + 1 , r , x , y) ;
            if(ans.fi > res.fi)  ans = res ;
            else if(ans.fi == res.fi)  ans.se += res.se ;
        }
        return ans ;
    }
} seg ;
vector<int> pos[maxn] ;
int main()
{
    ios ;
    ll ans = 0 ;
    cin >> n ;
    rep(i , 1 , n)  cin >> a[i] , pos[i].pb(n + 1) ;
    seg.build(1 , 1 , n) ;
    per(i , n , 1)
    {
        int x = a[i] ;
        int nxt = pos[x].back() ;
        seg.add(1 , 1 , n , i , nxt - 1 , 1) ;
        if(sz(pos[x]) >= 4)
        {
            int third = pos[x][sz(pos[x]) - 3] ;
            int fourth = pos[x][sz(pos[x]) - 4] ;
            seg.add(1 , 1 , n , third , fourth - 1 , 1) ;
        }
        pos[x].pb(i) ;
        if(sz(pos[x]) >= 4)
        {
            int third = pos[x][sz(pos[x]) - 3] ;
            int fourth = pos[x][sz(pos[x]) - 4] ;
            seg.add(1 , 1 , n , third , fourth - 1 , -1) ;
        }
        pll res = seg.query(1 , 1 , n , i , n) ;
        if(res.fi == 0)  ans += res.se ;
    }
    cout << ans << '\n' ;
    return 0 ;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值