• CF1290E Cartesian Tree


    给定一个 (1sim n) 的排列 (a) .

    对于一个整数 (kin[1,n]) , 将排列中 (leqslant k) 的项构成的子序列建大根笛卡尔树. 这棵笛卡尔树的所有节点的子树大小之和记为 (s_k) .

    (forall kin[1,n]) , 求 (s_k) .

    (1 le n le 150000)


    很套路的一道题。

    先考虑对于一棵笛卡尔树如何求出所有子树大小,我们用单调栈建树的过程中是发现一个比当前栈顶元素大的数就弹栈,作为他的父亲,那么设 (pre_i) 表示第一个向左比 (a_i) 大的数的位置, (nxt_i) 表示向右第一个比 (a_i) 大的位置,那么 (i) 的子树大小是 (nxt_i-pre_i-1)

    然后就可以分开考虑 (nxt)(pre) 了,考虑求 (pre) ,我们从小到大枚举 (k) ,每次插入 (k) ,设 (k) 的位置是 (pos_k) ,那么 ([1,pos_k-1]) 的数的 (nxt) 可能会改变,需要和 (pos_k') 取min( (pos_k')(k) 在新序列的位置),而 (k) 的加入使 ([pos_k+1,n]) 的数的位置加 (1) ,所以 (nxt) 要加 (1)

    这个东西可以用吉司机线段树来维护,至于插入数,我们多维护出这个区间有多少个数插入了就可以了。

    对于 (pre) ,直接把序列翻转,那么我们求的 (sum pre) 就变成了 (sum i-pre+1)(i) 是序列元素个数,最后把多算的减掉就好了。

    Code

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    const int N = 2e5;
    const int inf = 1e9;
    using namespace std;
    int n,a[N + 5],pos[N + 5],s[N + 5];
    long long ans[N + 5];
    struct node
    {
        int mx,mxi,smx,s;
        long long sm;
        node operator +(const node &a)const
        {
            node c = {0,0,0,0,0};
            c.sm = sm + a.sm;
            c.s = s + a.s;
            if (mx > a.mx)
            {
                c.mx = mx;
                c.smx = smx;
                c.mxi = max(a.mx,mxi);
            }
            if (mx < a.mx)
            {
                c.mx = a.mx;
                c.smx = a.smx;
                c.mxi = max(a.mxi,mx);
            }
            if (mx == a.mx)
            {
                c.mx = mx;
                c.smx = a.smx + smx;
                c.mxi = max(a.mxi,mxi);
            }
            return c;
        }
    };
    struct Seg
    {
        node s[N * 4 + 5];
        int tag[N * 4 + 5],ct[N * 4 + 5];
        #define zrt k << 1
        #define yrt k << 1 | 1
        void build(int k,int l,int r)
        {
            s[k] = {0,-inf,0,0,0};tag[k] = 0;ct[k] = inf;
            if (l == r)
                return;
            int mid = l + r >> 1;
            build(zrt,l,mid);
            build(yrt,mid + 1,r);
        }
        void add(int k,int l,int r,int z)
        {
            if (!s[k].s)
                return;
            s[k].sm += 1ll * s[k].s * z;
            s[k].mx += z;
            if (s[k].mxi != -inf)
                s[k].mxi += z;
            tag[k] += z;
            if (ct[k] != inf)
                ct[k] += z;
        }
        void cha(int k,int z)
        {
            if (!s[k].s)
                return;
            if (z >= s[k].mx)
                return;
            s[k].sm -= 1ll * s[k].smx * (s[k].mx - z);
            s[k].mx = z;
            ct[k] = z;
        }
        void pushdown(int k,int l,int r,int mid)
        {
            if (tag[k])
            {
                add(zrt,l,mid,tag[k]);
                add(yrt,mid + 1,r,tag[k]);
                tag[k] = 0;
            }
            if (ct[k] != inf)
            {
                cha(zrt,ct[k]);
                cha(yrt,ct[k]);
                ct[k] = inf;
            }
        }
        void insert(int k,int l,int r,int x,int z)
        {
            if (l == r)
            {
                s[k] = (node){z,-inf,1,1,z};
                return;
            }
            int mid = l + r >> 1;
            pushdown(k,l,r,mid);
            if (x <= mid)
                insert(zrt,l,mid,x,z);
            else
                insert(yrt,mid + 1,r,x,z);
            s[k] = s[zrt] + s[yrt];
        }
        void modify(int k,int l,int r,int x,int y,int z)
        {
            if (l >= x && r <= y)
            {
                add(k,l,r,z);
                return;
            }
            int mid = l + r >> 1;
            pushdown(k,l,r,mid);
            if (x <= mid)
                modify(zrt,l,mid,x,y,z);
            if (y > mid)
                modify(yrt,mid + 1,r,x,y,z);
            s[k] = s[zrt] + s[yrt];
        }
        void getmin(int k,int l,int r,int x,int y,int z)
        {
            if (l >= x && r <= y)
            {
                if (z > s[k].mxi)
                {
                    cha(k,z);
                    return;
                }
            }
            int mid = l + r >> 1;
            pushdown(k,l,r,mid);
            if (x <= mid)
                getmin(zrt,l,mid,x,y,z);
            if (y > mid)
                getmin(yrt,mid + 1,r,x,y,z);
            s[k] = s[zrt] + s[yrt];
        }
        int query(int k,int l,int r,int x,int y)
        {
            if (l >= x && r <= y)
                return s[k].s;
            int mid = l + r >> 1;
            pushdown(k,l,r,mid);
            if (y <= mid)
                return query(zrt,l,mid,x,y);
            if (x > mid)
                return query(yrt,mid + 1,r,x,y);
            return query(zrt,l,mid,x,y) + query(yrt,mid + 1,r,x,y);
        }
    }tree;
    void solve()
    {
        for (int i = 1;i <= n;i++)
            pos[a[i]] = i;
        tree.build(1,1,n);
        memset(s,0,sizeof(s));
        for (int k = 1;k <= n;k++)
        {
            if (pos[k] + 1 <= n)
                tree.modify(1,1,n,pos[k] + 1,n,1);
            if (pos[k] - 1 >= 1)
            {
                int x = tree.query(1,1,n,1,pos[k] - 1) + 1;
                tree.getmin(1,1,n,1,pos[k] - 1,x);
            }
            tree.insert(1,1,n,pos[k],k + 1);
            ans[k] += tree.s[1].sm;
        }
    }
    int main()
    {
        scanf("%d",&n);
        for (int i = 1;i <= n;i++)
            scanf("%d",&a[i]);
        solve();
        reverse(a + 1,a + n + 1);
        solve();
        for (int i = 1;i <= n;i++)
            printf("%lld
    ",ans[i] - 1ll * i * (i + 2));
        return 0;
    }
    
  • 相关阅读:
    IIS 添加二级应用程序
    VS中发布并调试IIS程序
    未启用当前数据库的 SQL Server Service Broker,因此查询通知不受支持。如果希望使用通知,请为此数据库启用 Service Broker
    Flash基础开发习惯指要
    2012云计算扫盲
    flash问题集锦(新手必看)
    Flash常用ActionScript控制语句基本用法祥解
    通过offset值的设置使html元素对齐
    不用float也可以让div横向显示
    QQ空间里写的开发心得
  • 原文地址:https://www.cnblogs.com/sdlang/p/14491916.html
Copyright © 2020-2023  润新知