Codeforces 672 E:Ultimate Weirdness of an Array 线段树 ★ ★ ★

题意是求所有区间[i,j],f(i,j)的和。f(i,j)表示除去i,j的区间之外的数其中两个最大的gcd。


ai是从1到200000的数,对于每一个i维护一个区间h(j,k),h(j,k)的含义是f(j,k)<=i,对于每一个j,找最小的k。

然后对于i来说的话,gcd<=i的区间数目就可以枚举j,对每一个j算相应的k值,也就是题解中的next[j]值。sum(n-next[j]+1) (1<=j<=n)

可以发现就是找能够整除这个数的最左距离,次左距离,次右距离,最右距离。

之后就是使用线段树更新这个和了。

#include <set>
#include <map>
#include <stack>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define L(i) i<<1
#define R(i) i<<1|1
#define INF  0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-9
#define maxn 200010
#define MOD 1000000007
typedef long long LL;

struct node
{
    int l,r;
    int Min,Max,lazy;
    long long sum;
} tree[maxn*4];

int n,a[maxn],idx[maxn];
int l1[maxn],l2[maxn],r1[maxn],r2[maxn];
long long tmp[maxn];

void pushup(int pos)
{
    tree[pos].Max = max(tree[pos<<1].Max,tree[pos<<1|1].Max);
    tree[pos].Min = min(tree[pos<<1].Min,tree[pos<<1|1].Min);
    tree[pos].sum = tree[pos<<1].sum + tree[pos<<1|1].sum;
}
void pushdown(int pos)
{
    if(!tree[pos].lazy)
        return;
    tree[pos<<1].Max = tree[pos<<1|1].Max = tree[pos].lazy;
    tree[pos<<1].Min = tree[pos<<1|1].Min = tree[pos].lazy;
    tree[pos<<1].lazy = tree[pos<<1|1].lazy = tree[pos].lazy;
    tree[pos<<1].sum = (long long)(tree[pos<<1].r - tree[pos<<1].l+1) * tree[pos].lazy;
    tree[pos<<1|1].sum = (long long)(tree[pos<<1|1].r-tree[pos<<1|1].l+1) * tree[pos].lazy;
    tree[pos].lazy = 0;
}

void build(int l,int r,int pos)
{
    tree[pos].l = l;
    tree[pos].r = r;
    tree[pos].lazy = 0;
    if(l == r)
    {
        tree[pos].Max = tree[pos].Min = l;
        tree[pos].sum = (long long)l;
        return;
    }
    int mid = (l+r)/2;
    build(l,mid,pos<<1);
    build(mid+1,r,pos<<1|1);
    pushup(pos);
}                                                   //建树                                                  //查询操作

void update(int l,int r,int pos,int val)
{
    if(l > r)
        return ;
    if(tree[pos].Min >= val)                           //这里需要注意单独写
        return;
    if(tree[pos].l == l && tree[pos].r == r && tree[pos].Max <= val) //<span style="font-family:SimSun;">这里需要写在判断上,不能写在里面</span>
    {
        tree[pos].sum = (long long)(r-l+1) * val;
        tree[pos].Max = tree[pos].Min = val;
        tree[pos].lazy = val;
        return ;
    }
    pushdown(pos);
    int mid = (tree[pos].l + tree[pos].r) >> 1;
    if(r <= mid)
        update(l,r,pos<<1,val);
    else if(l > mid)
        update(l,r,pos<<1|1,val);
    else
    {
        update(l,mid,pos<<1,val);
        update(mid+1,r,pos<<1|1,val);
    }
    pushup(pos);
}                                         //自上而下更新节点


int main()
{
    int t;
    while(scanf("%d",&n) != EOF)
    {
        memset(l1,0,sizeof(l1));
        memset(l2,0,sizeof(l2));
        memset(r1,0,sizeof(r1));
        memset(r2,0,sizeof(r2));
        for(int i = 1; i <= n; i++)
        {
            scanf("%d",&a[i]);
            idx[a[i]] = i;
        }
        for(int i = 1; i <= maxn; i++)
            for(int j = i; j <= maxn; j+=i)
                if(idx[j])
                {
                    if(!l1[i] || l1[i] > idx[j])
                    {
                        l2[i] = l1[i];
                        l1[i] = idx[j];
                    }
                    else if(!l2[i] || l2[i] > idx[j])
                        l2[i] = idx[j];
                    if(!r1[i] || r1[i] < idx[j])
                    {
                        r2[i] = r1[i];
                        r1[i] = idx[j];
                    }
                    else if(!r2[i] || r2[i] < idx[j])
                        r2[i] = idx[j];
                }
        build(1,n,1);
        for(int i = maxn; i > 0; i--)
        {
            if(l1[i] != r1[i])
            {
                update(1,l1[i],1,r2[i]);
                update(l1[i]+1,l2[i],1,r1[i]);
                update(l2[i]+1,n,1,n+1);
            }
            tmp[i] = n*(n+1) - tree[1].sum;
        }
        long long ans = 0;
        for(int i = 1; i < maxn; i++)
            ans += (long long)i * (tmp[i+1] - tmp[i]);
        printf("%lld\n",ans);
    }
    return 0;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值