dtoi4702 Gcd

血红的双手。 提交于 2020-02-03 22:28:48

题意:

     有一个长度为n的互不相同的序列,求对于任意i,j,1<=i<=j<=n,求g(i,j)。

     g(i,j)的定义是将i~j的元素都删除之后剩余的数字两两之间gcd的最大值。

题解:

     首先枚举gcd,考虑什么时候会作为答案。

     找到它的倍数所在的位置,假设从小到大所在的位置为a[0],a[1]...a[k],那么只需要有a[0],a[1]存在或者a[k-1],a[k]存在或者a[0],a[k]存在,然后其它区间乱删都没有关系。

     那么现在的问题就在于“区间乱删”时,会有好多之前已经计算过的答案被算了多次,显然是不行的。

     仔细思考我们要解决什么问题,现在我们需要做的事情就是给定一个区间l,r求l,r中剩余的子区间还有多少个。

     那我们先对于每一个位置i,记t[i]表示[i,i],[i,i+1]...[i,t[i]]这些区间已经被算过了。不难发现t[i]是单调的,因为如果大区间算过了,小区间一定也被算过了。

     那么l,r中剩余的子区间还有多少个要如何计算呢,其实答案就是[l,r]中,对于所有满足(t[i]<=r)的i,r-t[i]的总和。由于t[i]是单调的,所以在线段树上二分一下再求个和就行了。

     接下来我们把[l,r]中所有t[i]<=r的设置为t[i]=r,这依然可以线段树解决。于是就做完了这道题。

#include<cstdio>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int INF=1e9;
int T,n,a[200002],min1[200002],min2[200002],max1[200002],max2[200002];
typedef struct{
    int Min,f;
    long long sum;
}P;
P p[800002];
long long ans;
void build(int root,int begin,int end){
    if (begin==end)
    {
        p[root].Min=p[root].sum=begin-1;
        p[root].f=0;return;
    }
    int mid=(begin+end)/2;
    build(root*2,begin,mid);build(root*2+1,mid+1,end);
    p[root].Min=min(p[root*2].Min,p[root*2+1].Min);
    p[root].sum=p[root*2].sum+p[root*2+1].sum;
    p[root].f=0;
}
void pushdown(int root,int begin,int mid,int end){
    if (p[root].f)
    {
        p[root*2].Min=p[root].f;p[root*2+1].Min=p[root].f;
        p[root*2].sum=(long long)p[root].f*(mid-begin+1);p[root*2+1].sum=(long long)p[root].f*(end-mid);
        p[root*2].f=p[root].f;p[root*2+1].f=p[root].f;
        p[root].f=0;
    }
}
void gengxin(int root,int begin,int end,int begin2,int end2,int wz){
    if (begin>end2 || end<begin2)return;
    if (begin>=begin2 && end<=end2)
    {
        p[root].Min=p[root].f=wz;
        p[root].sum=(long long)wz*(end-begin+1);
        return;
    }
    int mid=(begin+end)/2;pushdown(root,begin,mid,end);
    gengxin(root*2,begin,mid,begin2,end2,wz);gengxin(root*2+1,mid+1,end,begin2,end2,wz);
    p[root].Min=min(p[root*2].Min,p[root*2+1].Min);
    p[root].sum=p[root*2].sum+p[root*2+1].sum;
}
int cx(int root,int begin,int end,int z){
    if (begin==end)return begin;
    int mid=(begin+end)/2;pushdown(root,begin,mid,end);
    if (p[root*2+1].Min<=z)return cx(root*2+1,mid+1,end,z);
    else return cx(root*2,begin,mid,z);
}
int chaxun(int root,int begin,int end,int begin2,int end2){
    if (begin>end2 || end<begin2)return begin2-1;
    if (begin>=begin2 && end<=end2)
    {
        if (p[root].Min<=end2)return cx(root,begin,end,end2);
        return begin2-1;
    }
    int mid=(begin+end)/2;pushdown(root,begin,mid,end);
    int t1=chaxun(root*2+1,mid+1,end,begin2,end2);
    if (t1>=begin2)return t1;
    else return chaxun(root*2,begin,mid,begin2,end2);
}
long long cxsum(int root,int begin,int end,int begin2,int end2){
    if (begin>end2 || end<begin2 || begin2>end2)return 0;
    if (begin>=begin2 && end<=end2)return p[root].sum;
    int mid=(begin+end)/2;pushdown(root,begin,mid,end);
    return cxsum(root*2,begin,mid,begin2,end2)+cxsum(root*2+1,mid+1,end,begin2,end2); 
}
long long js(int x,int y){
    if (x<0 || x>n || y<0 || y>n || x==y)return 0;
    long long ans=0;
    if (x>1)
    {
        int ef=chaxun(1,1,n,1,x-1);
        ans+=((long long)(x-1)*ef-cxsum(1,1,n,1,ef));
        gengxin(1,1,n,1,ef,x-1);
    }
    if (x+1<=y-1)
    {
        int ef=chaxun(1,1,n,x+1,y-1);
        ans+=((long long)(y-1)*(ef-x)-cxsum(1,1,n,x+1,ef));
        gengxin(1,1,n,x+1,ef,y-1);
    }
    if (y<n)
    {
        int ef=chaxun(1,1,n,y+1,n);
        ans+=((long long)n*(ef-y)-cxsum(1,1,n,y+1,ef));
        gengxin(1,1,n,y+1,ef,n);
    }
    
    return ans;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);int Max=0;ans=0;
        for (int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            Max=max(Max,a[i]);
        }
        for (int i=1;i<=Max;i++)
        {
            min1[i]=min2[i]=INF;
            max1[i]=max2[i]=-INF;
        }
        for (int i=1;i<=n;i++)
        {
            if (min1[a[i]]==INF)min1[a[i]]=i;
            else if (min2[a[i]]==INF)min2[a[i]]=i;
        }
        for (int i=n;i>=1;i--)
        {
            if (max1[a[i]]==-INF)max1[a[i]]=i;
            else if (max2[a[i]]==-INF)max2[a[i]]=i;
        }
        build(1,1,n);
        for (int i=Max;i>=1;i--)
        {
            int mi1=INF,mi2=INF,mx1=-INF,mx2=-INF;
            for (int j=i;j<=Max;j+=i)
            {
                if (min1[j]<mi1)
                {
                    mi2=mi1;mi1=min1[j];
                }
                else mi2=min(mi2,min1[j]);
                if (min2[j]<mi1)
                {
                    mi2=mi1;mi1=min2[j];
                }
                else mi2=min(mi2,min2[j]);
                if (max1[j]>mx1)
                {
                    mx2=mx1;mx1=max1[j];
                }
                else mx2=max(mx2,max1[j]);
                if (max2[j]>mx1)
                {
                    mx2=mx1;mx1=max2[j];
                }
                else mx2=max(mx2,max2[j]);
            }
            ans+=i*(js(mx2,mx1)+js(mi1,mx1)+js(mi1,mi2));
        }
        printf("%lld\n",ans);
    }
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!