Codeforces problem 671C Ultimate Weirdness of an Array - 线段树

  • 题意

    定义 F(l,r) 的值表示序列 A[1:n] 的子序列 A[1...(l1),(r+1)...n] 之中任意两个数的最大公约数的最大值。

  • 题解

    If we calculate an array H where Hi is how many (l - r)s there are that f(l,  r)  ≤  i, then we can easily calculate the answer.
    How to calculate array H. Let’s keep a vector, vi keeps all elements indexes which contain i as a divisor — in sorted order. We will iterate over max element to 1. When we are iterating we will keep another array next, Let’s suppose we are iterating over i, nextj will keep leftmost k where f(j,  k)  ≤  i. Sometimes there is no such k, then nextj will be n + 1. Hi will be equal to , because if we choose p as l, r must be at least nextp, so for l we can choose nnextp+1 different r s.
    Let’s look how we update next array when we iterate i to i - 1.
    Let vi be b1,b2,b3...bk . Note that our l - r must be cover at least k - 1 of this indexes. l must less or equal to b2 . So we have to maximize all nextp with n + 1 where p>b2 . Otherwise If lb1+1 then r must be at least bk, that’s we will maximize all nextps where b1<pb2 with bk. And finally for all nextp’s where (1pb1) we have to maximize them with bk1 . Observe that next array will be monotonic  -  in non decreasing order  -  after all operations. So we can easily make our updates with a segment tree that can perform following operations:
     - Returns rightmost index i where nexti is less than some k.
     - Returns sum of all elements in next array.
     - Can assign some k to all elements between some l and r.
    If all update and queries performed in O(logn) then overall complexity will be O(nlogn), we can also apply all this operations with STL set in same complexity.

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <bitset>
//#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;

#define ll long long
#define SZ(x) ((int)(x).size()) 
#define ALL(v) (v).begin(), (v).end()
#define foreach(i, v) for (__typeof((v).begin()) i = (v).begin(); i != (v).end(); ++ i)
#define reveach(i, v) for (__typeof((v).rbegin()) i = (v).rbegin(); i != (v).rend(); ++ i) 
#define REP(i,a,n) for ( int i=a; i<int(n); i++ )
#define FOR(i,a,n) for ( int i=n-1; i>= int(a);i-- )
#define lson rt<<1, L, m
#define rson rt<<1|1, m, R
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
#define mp(x, y) make_pair(x, y)
#define pb(x) push_back(x)
#define fi first
#define se second
#define CLR(a, b) memset(a, b, sizeof(a))
#define Max(a, b) a = max(a, b)
#define Min(a, b) a = min(a, b)
const int maxn = 2e5 + 7;
ll n;
ll a[maxn];
vector<ll> v[maxn];

ll sum[maxn << 2], mx[maxn << 2], mi[maxn << 2], lazy[maxn << 2];
ll H[maxn], inx[maxn];
ll L1[maxn], L2[maxn], R1[maxn], R2[maxn];

void pushUp(int rt){
    int lch = rt << 1, rch = rt << 1 | 1;
    mx[rt] = max(mx[lch], mx[rch]);
    mi[rt] = min(mi[lch], mi[rch]);
    sum[rt] = sum[lch] + sum[rch];
}
void pushDown(int rt, int L, int R){
    int lch = rt << 1, rch = rt << 1 | 1;
    int m = (L + R) >> 1;
    if(lazy[rt]){
        lazy[lch] = mx[lch] = mi[lch] = lazy[rt];
        lazy[rch] = mx[rch] = mi[rch] = lazy[rt];
        sum[lch] = lazy[rt] * (m - L);
        sum[rch] = lazy[rt] * (R - m);
        lazy[rt] = 0;
    }
}
void build(int rt, int L, int R){
    lazy[rt] = 0;
    if(L + 1 == R){
        mx[rt] = mi[rt] = sum[rt] = L;
        return ;
    }
    int m = (L + R) >> 1;
    build(lson);
    build(rson);
    pushUp(rt);
}
void update(int rt, int L, int R, int l, int r, ll val){
    if(l >= r) return ;
    if(mi[rt] >= val) return ;
    if(l <= L && R <= r && mx[rt] <= val){
        lazy[rt] = val;
        mi[rt] = mx[rt] = val;
        sum[rt] = (R - L) * val;
        return ;
    }
    pushDown(rt, L, R);
    int m = (L + R) >> 1;
    if(l < m) update(lson, l, r, val);
    if(r > m) update(rson, l, r, val);
    pushUp(rt);
}
int main(){
#ifdef ac
    freopen("in.txt","r",stdin);
#endif
    //freopen("out.txt","w",stdout);
    scanf("%lld", &n);
    REP(i, 0, n) scanf("%lld", &a[i]);
    CLR(inx, -1);
    CLR(L1, -1);
    CLR(L2, -1);
    CLR(R1, -1);
    CLR(R2, -1);
    REP(i, 0, n) inx[a[i]] = i;
    ///预处理三个区间
    REP(i, 1, maxn){
        for(int j = i; j < maxn; j += i){
            if(inx[j] != -1){
                if(L1[i] == -1 || L1[i] > inx[j]) L2[i] = L1[i], L1[i] = inx[j];
                else if(L2[i] == -1 || L2[i] > inx[j]) L2[i] = inx[j];
                if(R1[i] < inx[j]) R2[i] = R1[i], R1[i] = inx[j];
                else if(R2[i] < inx[j]) R2[i] = inx[j];
            }
        }
    }
    build(1, 0, n);
    FOR(i, 1, maxn){
        if(L1[i] != R1[i]){
            update(1, 0, n, 0, L1[i] + 1, R2[i]);
            update(1, 0, n, L1[i] + 1, L2[i] + 1, R1[i]);
            update(1, 0, n, L2[i] + 1, n, n);
        }
        H[i] = n * n - sum[1];
    }
    ll ans = 0;
    REP(i, 1, maxn - 1) ans += i * (H[i + 1] - H[i]);
    printf("%lld\n", ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值