• 题解-CF1418G Three Occurrences


    题面

    CF1418G Three Occurrences

    给一个 (n) 个数的序列 (a_i),求每个出现过的数出现次数为 (3) 的子序列个数。

    数据范围:(1le nle 5cdot 10^5)(1le a_ile n)


    蒟蒻语

    做了半个上午做出了最离谱的做法:表格打叉叉。

    用线段树求矩阵面积并实现,时间复杂度 (Theta(nlog n)),总用时 (1.40 min)


    蒟蒻解

    想象有一个 (n imes n) 的表格,行表示右端点,列表示左端点。

    如果表格上的数为 (1) 表示这个区间没被叉掉,对答案有贡献,否则这个区间不符合题目要求。

    可以发现每次叉的都是一个矩阵,线段树扫描线求矩阵面积并即可。

    考虑数据:

    8
    1 1 2 1 2 1 2 1
    
    answer=2
    

    现在的表表是:

    (1) (1) (1) (1) (1) (1) (1) (1)
    (1) (1) (1) (1) (1) (1) (1) (1)
    (1) (1) (1) (1) (1) (1) (1) (1)
    (1) (1) (1) (1) (1) (1) (1) (1)
    (1) (1) (1) (1) (1) (1) (1) (1)
    (1) (1) (1) (1) (1) (1) (1) (1)
    (1) (1) (1) (1) (1) (1) (1) (1)
    (1) (1) (1) (1) (1) (1) (1) (1)

    ((1)) 把左端点大于右端点的给叉了:

    (1) (0) (0) (0) (0) (0) (0) (0)
    (1) (1) (0) (0) (0) (0) (0) (0)
    (1) (1) (1) (0) (0) (0) (0) (0)
    (1) (1) (1) (1) (0) (0) (0) (0)
    (1) (1) (1) (1) (1) (0) (0) (0)
    (1) (1) (1) (1) (1) (1) (0) (0)
    (1) (1) (1) (1) (1) (1) (1) (0)
    (1) (1) (1) (1) (1) (1) (1) (1)

    ((2)) 把超过 (3) 个相同的数的区间叉了:

    (1) (0) (0) (0) (0) (0) (0) (0)
    (1) (1) (0) (0) (0) (0) (0) (0)
    (1) (1) (1) (0) (0) (0) (0) (0)
    (1) (1) (1) (1) (0) (0) (0) (0)
    (1) (1) (1) (1) (1) (0) (0) (0)
    (0) (1) (1) (1) (1) (1) (0) (0)
    (0) (1) (1) (1) (1) (1) (1) (0)
    (0) (0) (1) (1) (1) (1) (1) (1)

    ((3)) 把少于 (3) 个相同的数的区间叉了:

    (0) (0) (0) (0) (0) (0) (0) (0)
    (0) (0) (0) (0) (0) (0) (0) (0)
    (0) (0) (0) (0) (0) (0) (0) (0)
    (0) (0) (0) (0) (0) (0) (0) (0)
    (0) (0) (0) (0) (0) (0) (0) (0)
    (0) (0) (0) (0) (0) (0) (0) (0)
    (0) (1) (0) (0) (0) (0) (0) (0)
    (0) (0) (1) (0) (0) (0) (0) (0)

    然后剩下的 (2) 就是答案了。

    手玩一下就可以发现叉的规律了,非常简单易懂,蒟蒻就不负责任地放代码里了。


    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    //Start
    typedef long long ll;
    typedef double db;
    #define mp(a,b) make_pair((a),(b))
    #define x first
    #define y second
    #define be(a) (a).begin()
    #define en(a) (a).end()
    #define sz(a) int((a).size())
    #define pb(a) push_back(a)
    #define R(i,a,b) for(int i=(a),I=(b);i<I;i++)
    #define L(i,a,b) for(int i=(b)-1,I=(a)-1;i>I;i--)
    const int iinf=0x3f3f3f3f;
    const ll linf=0x3f3f3f3f3f3f3f3f;
    
    //Data
    const int N=5e5;
    int n,a[N],d[N]; ll ans;
    vector<int> id[N];
    struct line{
        int l,r,t; line(){}
        line(int l,int r,int t):l(l),r(r),t(t){}
    };
    vector<line> p[N+1];
    void addmat(int xl,int xr,int yl,int yr){
        // cout<<"(["<<xl<<","<<xr<<"),["<<yl<<","<<yr<<"))
    ";
        p[xl].pb(line(yl,yr,1)),p[xr].pb(line(yl,yr,-1));
    }
    
    //SegmentTree
    const int tN=N<<2;
    #define mid ((l+r)>>1)
    int mn[tN],mc[tN],mk[tN];
    void pushup(int k){
        if(mn[k*2+1]<mn[k*2+2]) mn[k]=mn[k*2+1],mc[k]=mc[k*2+1];
        else if(mn[k*2+1]>mn[k*2+2]) mn[k]=mn[k*2+2],mc[k]=mc[k*2+2];
        else mn[k]=mn[k*2+1],mc[k]=mc[k*2+1]+mc[k*2+2];
    }
    void pushadd(int k,int v){mn[k]+=v,mk[k]+=v;}
    void pushdown(int k){pushadd(k*2+1,mk[k]),pushadd(k*2+2,mk[k]),mk[k]=0;}
    void build(int k=0,int l=0,int r=n){
        if(r-l==1) return mn[k]=0,mc[k]=1,void();
        build(k*2+1,l,mid),build(k*2+2,mid,r),pushup(k);
    }
    void add(int x,int y,int v,int k=0,int l=0,int r=n){
        if(r<=x||y<=l) return; if(x<=l&&r<=y) return pushadd(k,v);
        pushdown(k),add(x,y,v,k*2+1,l,mid),add(x,y,v,k*2+2,mid,r),pushup(k);
    }
    
    //Main
    int main(){
        ios::sync_with_stdio(0);
        cin.tie(0),cout.tie(0);
        cin>>n;
        R(i,0,n) id[i].pb(-1),id[i].pb(-1),id[i].pb(-1);
        R(i,0,n) cin>>a[i],--a[i],d[i]=sz(id[a[i]]),id[a[i]].pb(i);
        R(i,0,n) id[i].pb(n),id[i].pb(n),id[i].pb(n);
        // cout<<"ok 60
    ";
        R(i,0,n){
            addmat(i,i+1,i+1,n); // 把左端点大于右端点的给叉了
            addmat(i,n,0,id[a[i]][d[i]-3]+1); //把超过 3 个相同的数的区间叉了
            addmat(i,id[a[i]][d[i]+2],id[a[i]][d[i]-1]+1,i+1); //把少于 3 个相同的数的区间叉了
        }
        // cout<<"ok 66
    ";
        build();
        R(i,0,n){ //求个矩阵面积并
            for(line u:p[i])if(u.r>u.l) add(u.l,u.r,u.t);
            ans+=mc[0]*(mn[0]==0);
        }
        cout<<ans<<'
    ';
        return 0;
    }
    

    祝大家学习愉快!

  • 相关阅读:
    量子计算机还要忽悠多少年?[转载]
    量子计算机的七大惊人颠覆
    Windows10共享文件夹、打印机,可是网络上显示“未授予用户在此计算机上的请求登录类型”的解决方案
    深圳绿道-观澜段-乡村一号
    深圳绿道最全资料合集
    Office2013激活工具
    恢复桌面快捷方式小箭头最简单的方法
    css hack
    字体
    移动端 meta 标签笔记
  • 原文地址:https://www.cnblogs.com/George1123/p/13749148.html
Copyright © 2020-2023  润新知