zoukankan      html  css  js  c++  java
  • CF671C Ultimate Weirdness of an Array

    n<=200000个数(<=200000),问所有的f(i,j)的和,f(i,j)表示去掉区间i到j后的剩余的数字中任选两个数的最大gcd

    首先我们用(nxt_l)表示当(f(l,r)le i)时,(l)对应的右端点(r)最往左能取到哪里,初始时(nxt_l=l),如果不存在合法右端点就记(nxt_l=n+1)

    然后考虑从大到小枚举(i),当从(f(l,r)le i)变为(f(l,r)le i-1)时,我们需要删除(gcd=i)的贡献,找出(i)的倍数在原序列中的下标记为(x_1,x_2......x_m),而我们新得到的区间([l,nxt_l'])肯定是要满足包含(m-1)(x_i)的,不然答案就至少是(i)了,所以我们进行如下修改操作:

    • (lin(x_2,n])(nxt_l=n+1)

    • (lin(x_1,x_2])(nxt_l=max(nxt_l,x_m))

    • (lin[1,x_1])(nxt_l=max(nxt_l,x_{m-1}))

    2,3操作看似需要吉司机线段树来维护,但是我们冷静下来分析一波,(nxt)数组一定是单调不降,所以一定是进行区间的一段前缀覆盖,直接线段树上二分修改复杂度就是(O(nlogn))

    其实开始时(O(nsqrt n))处理每个数倍数的位置就已经足够了,但是也可以优化,因为我们最多只需要2个开头的和2个结尾的,那么直接合并(i)的倍数的集合就好了,复杂度是(O(nlogn)),我比较懒就直接写的第一种QAQ

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    #define zrt k << 1
    #define yrt k << 1 | 1
    const int N = 2e5;
    using namespace std;
    int n,a[N + 5],Mx;
    long long ans,las;
    vector <int> d[N + 5];
    struct Seg
    {
        long long sm[N * 5 + 5],tag[N * 4 + 5];
        int mi[N * 4 + 5],mx[N * 4 + 5];
        void pushup(int k)
        {
            sm[k] = sm[zrt] + sm[yrt];
            mi[k] = min(mi[zrt],mi[yrt]);
            mx[k] = max(mx[zrt],mx[yrt]);
        }
        void build(int k,int l,int r)
        {
            if (l == r)
            {
                mx[k] = mi[k] = sm[k] = l;
                return;
            }
            int mid = l + r >> 1;
            build(zrt,l,mid);
            build(yrt,mid + 1,r);
            pushup(k);
        }
        void cha(int k,int l,int r,int z)
        {
            sm[k] = 1ll * (r - l + 1) * z;
            mi[k] = mx[k] = z;
            tag[k] = z;
        }
        void pushdown(int k,int l,int r,int mid)
        {
            if (tag[k])
            {
                cha(zrt,l,mid,tag[k]);
                cha(yrt,mid + 1,r,tag[k]);
                tag[k] = 0;
            }
        }
        void modify(int k,int l,int r,int x,int y,int z)
        {
            if (x > y)
                return;
            if (r < x || l > y || mi[k] >= z)
                return;
            if (l >= x && r <= y && mx[k] <= z)
            {
                cha(k,l,r,z);
                return;
            }
            if (l == r)
            {
                sm[k] = mi[k] = mx[k] = z;
                return;
            }
            int mid = l + r >> 1;
            pushdown(k,l,r,mid);
            modify(zrt,l,mid,x,y,z);
            modify(yrt,mid + 1,r,x,y,z);
            pushup(k);
        }
    }tree;
    int main()
    {
        scanf("%d",&n);
        for (int i = 1;i <= n;i++)
            scanf("%d",&a[i]),Mx = max(Mx,a[i]);
        for (int i = 1;i <= n;i++)
            for (int j = 1;j * j <= a[i];j++)
                if (a[i] % j == 0)
                {
                    d[j].push_back(i);
                    if (j * j != a[i])
                        d[a[i] / j].push_back(i);
                }
        tree.build(1,1,n);
        las = 1ll * n * (n + 1) - tree.sm[1];
        int x1,x2,x3,x4;
        for (int i = Mx;i >= 1;i--)
        {
            if (d[i].size() < 2)
                continue;
            x1 = *d[i].begin();
            x2 = *(d[i].begin() + 1);
            x3 = *(d[i].end() - 2);
            x4 = *(d[i].end() - 1);
            tree.modify(1,1,n,x2 + 1,n,n + 1);
            tree.modify(1,1,n,x1 + 1,x2,x4);
            tree.modify(1,1,n,1,x1,x3);
            ans += (las - (1ll * n * (n + 1) - tree.sm[1])) * i;
            las = 1ll * n * (n + 1) - tree.sm[1];
        }
        cout<<ans<<endl;
        return 0;
    }
    
  • 相关阅读:
    临摹帖
    PC+PLC通过Modbus协议构建工控系统
    mvn打包时出现一个子模块找不到另一个子模块的情况
    T-SQL——函数——字符串操作函数
    T-SQL——透视PIVOT动态获取待扩展元素集
    T-SQL——数据透视和逆透视
    T-SQL——关于表数据的复制插入
    T-SQL——关于SQL读取Excel文件
    T-SQL——关于SqlPrompt的使用
    微信小程序--聊天室小程序(云开发)
  • 原文地址:https://www.cnblogs.com/sdlang/p/13435078.html
Copyright © 2011-2022 走看看