关于用线段树去纪录区间的一个问题

codeforces contest671的C题。

#include<bits/stdc++.h>//对函数f(i,j):left。right表示的是i的取值范围。此时j的取值为1-k,其中k>=minn[n]&&k<=maxn[n],且k(minn)=minn[n],k(maxn)=maxn[n]
typedef long long ll;
using namespace std;
ll tree[200010<<2];
int minn[200010<<2];
int maxn[200010<<2];
int add[200010<<2];
void pushDown(int temp,int left,int mid,int right,int temps)
{
    tree[temp<<1]=1LL*temps*(mid-left+1);
    tree[temp<<1|1]=1LL*temps*(right-mid);
    add[temp<<1]=temps;
    add[temp<<1|1]=temps;
    maxn[temp<<1]=maxn[temp<<1|1]=minn[temp<<1]=minn[temp<<1|1]=temps;
    add[temp]=0;
}
void ad(int i,int j,int temp,int left,int right,int n)
{
    if(temp<=minn[n])
        return;
    if(left>=i&&right<=j&&temp>=maxn[n])
    {
        tree[n]=1LL*(right-left+1)*temp;
        add[n]=maxn[n]=minn[n]=temp;
        return;
    }
    int mid=(left+right)/2;
    if(add[n])
        pushDown(n,left,mid,right,add[n]);
    if(j<=mid)
        ad(i,j,temp,left,mid,n<<1);
    else if(i>mid)
        ad(i,j,temp,mid+1,right,n<<1|1);
    else
    {
        ad(i,mid,temp,left,mid,n<<1);
        ad(mid+1,j,temp,mid+1,right,n<<1|1);
    }
    tree[n]=tree[n<<1]+tree[n<<1|1];
    maxn[n]=max(maxn[n<<1],maxn[n<<1|1]);
    minn[n]=min(minn[n<<1],minn[n<<1|1]);
}
vector<int>v;
int l[200010][2],r[200010][2];
ll ans[200010];
void deel(int pos,int val)
{
    if(l[val][0]>pos||l[val][0]==0)
    {
        l[val][1]=l[val][0];
        l[val][0]=pos;
    }
    else if(l[val][1]>pos||l[val][1]==0)
        l[val][1]=pos;
    if(r[val][0]<pos)
    {
        r[val][1]=r[val][0];
        r[val][0]=pos;
    }
    else if(r[val][1]<pos)
        r[val][1]=pos;
}
int main()
{
    int n;
    cin>>n;
    int val;
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&val);
        for(int j=1; j*j<=val; j++)
        {
            if(val%j==0)
            {
                deel(i,j);
                if(j*j!=val)
                    deel(i,val/j);
            }
        }
    }
    for(int i=1; i<=n; i++)
        ad(i,i,i-1,1,n,1);
    for(int i=200001; i>0; i--)
    {
        if(l[i][0]!=r[i][0])
        {
            ad(1,l[i][0],r[i][1]-1,1,n,1);//当i在1-l[i][0]之间是,j必须小于等于r[i][1]-1才能保证把所有结果等于i的区间都包含进去了
            ad(l[i][0]+1,l[i][1],r[i][0]-1,1,n,1);//同理
            if(l[i][1]!=n)
                ad(l[i][1]+1,n,n,1,n,1);//同理,此时有可能l[i][1]==n
        }
        //此时线段树中存储的是使结果大于等于i的区间数目
        ans[i]=1LL*n*n-tree[1];//所以ans[i]存储的是结果小于i的区间数目
    }
    ll fin=0;
    for(int i=1;i<=200000;i++)
        fin+=(ans[i+1]-ans[i])*i;
    printf("%I64d
",fin);
    return 0;
}
原文地址:https://www.cnblogs.com/lthb/p/5539224.html