jzoj6374. 【NOIP2019模拟2019.10.04】结界[生与死的境界]

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

赛时

花了近2h来课这道题.
发现了贪心规律,本想着可以水水70分。
然鹅发现数据过大,似乎要打高精度?
心态崩了,最后连20分都没拿到。
后来才发现一个小小的性质,不用打高精度。
太菜了。

题解

首先我们画画柿子。
答案即为\(\sum_{i=l}^r a_i*2^{k_i}\)
其中可以控制的就是那个k数组。
考虑贪心。
我们发现,对于一个正数,当然是希望它的k值越大。
对于一个负数,当然希望它的k值越小。
那么对于每个负数,它的k值最小为1(第一个位置为0)
然后一段正数区间则是递增的。

当然,这样是错误滴。为什么呢?我们有可能把一段正数区间乘2然后合并到前面的负数,这样答案会变大。
这样一直合并合并之后,可能会出现的情况是:k数组会分成一个一个块,每块开头是1(第一块开头为0)然后向后面递增。

所以我们从左到由依次加入数字,然后把当前的数字往前面合并,如果合并到最后当前块为负数就不合并了。
但是数值要去模啊?怎么办?我们发现,负数不会特别小,最少是-2e9,那么只要正数大于4e9的时候就可以往前面疯狂合并即可。
于是我们多记录一个数值表示当前块的数值堆4e9取min即可。

拿到70分的好成绩。

对于100分呢?
我们离线询问,把询问按照有端点排序,然后依次加入数字,做与上面相同的东东。
然后由于询问的左端点不是1,那么就不能直接统计了(废话)
那么这个左端点必定把某一块分割成两个部分。
求出这一块右边部分即可。

那么问题来了:分割后两个部分右边的部分不会被合并吗?
答案是:不会。
因为我们发现,左端点所在的块右边的块的数值是小于0的(否则在做的过程中就已经合并了),我们知道,数值小于0是不能合并的,因此不会合并。

那么利用并查集维护块或是线段树即可。

标程

#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <cctype>
using namespace std;
const int maxn=500010;
const long long mo=1000000007;

struct node{
    long long le,ri,kk,gg,lazy;
};

int n,m,l[maxn],r[maxn],gs,now;
long long a[maxn],b[maxn],c[maxn],mi[maxn],sum[maxn],jl,jr,jk,jg;
long long le[maxn],ri[maxn],k[maxn],zs[maxn],aa[maxn],qsum[maxn],col[maxn],id[maxn],wz[maxn],jll[maxn],qh[maxn],zd[maxn];
node tree[4*maxn];

void down_lazy(int x)
{
    if (tree[x].lazy==1)
    {
        tree[x*2].le=tree[x].le;
        tree[x*2+1].le=tree[x].le;
        tree[x*2].ri=tree[x].ri;
        tree[x*2+1].ri=tree[x].ri;
        tree[x*2].kk=tree[x].kk;
        tree[x*2+1].kk=tree[x].kk;
        tree[x*2].lazy=tree[x].lazy;
        tree[x*2+1].lazy=tree[x].lazy;
        tree[x*2].gg=tree[x].gg;
        tree[x*2+1].gg=tree[x].gg;
        tree[x].lazy=0;
    }
}

void change(int x,int l,int r,int st,int en,int le,int ri,int kk,int g)
{
    if (l==st && r==en)
    {
        tree[x].le=le;
        tree[x].ri=ri;
        tree[x].kk=kk;
        tree[x].gg=g;
        tree[x].lazy=1;
    }
    else
    {
        down_lazy(x);
        int mid=(l+r)/2;
        if (mid>=en) change(x*2,l,mid,st,en,le,ri,kk,g);
        else if (mid<st) change(x*2+1,mid+1,r,st,en,le,ri,kk,g);
        else
        {
            change(x*2,l,mid,st,mid,le,ri,kk,g);
            change(x*2+1,mid+1,r,mid+1,en,le,ri,kk,g);
        }
    }
}

void find(int x,int l,int r,int st)
{
    if (l==r)
    {
        jl=tree[x].le;
        jr=tree[x].ri;
        jk=tree[x].kk;
        jg=tree[x].gg;
    }
    else
    {
        down_lazy(x);
        int mid=(l+r)/2;
        if (mid>=st) find(x*2,l,mid,st);
        else find(x*2+1,mid+1,r,st);
    }
}

long long min(long long a,long long b)
{
    if (a<b) return a;return b;
}

void qsort(int ll,int rr)
{
    int i=ll;int j=rr;
    long long m=r[(i+j)/2];
    while (i<=j)
    {
        while (r[i]<m) i++;
        while (r[j]>m) j--;
        if (i<=j)
        {
            swap(r[i],r[j]);
            swap(l[i],l[j]);
            swap(wz[i],wz[j]);
            i++;j--;
        }
    }
    if (ll<j) qsort(ll,j);
    if (rr>i) qsort(i,rr); 
}

long long qsm(long long a,long long b)
{
    long long t=1;
    long long y=a;
    while (b>0)
    {
        if ((b&1)==1) t=t*y%mo;
        y=y*y%mo;
        b/=2;
    }
    return t;
}

int main()
{
//  freopen("data.in","r",stdin);
//  freopen("data.out","w",stdout);
    freopen("standard.in","r",stdin);
    freopen("standard.out","w",stdout);
    mi[0]=1;
    for (int i=1;i<=500000;i++)
    {
        mi[i]=mi[i-1]*2%mo;
    }
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
    {
        scanf("%l64d",&a[i]);
        zd[i]=(zd[i-1]+a[i]*mi[i]%mo)%mo;
    }
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d",&l[i],&r[i]);
    }
    for (int i=1;i<=m;i++)
    {
        wz[i]=i;
    }
    qsort(1,m);
    k[1]=0;le[1]=1;ri[1]=1;zs[1]=a[1];gs=1;aa[1]=a[1];qh[1]=(a[1]+mo)%mo;
    change(1,1,n,le[gs],ri[gs],le[gs],ri[gs],k[gs],gs);
    int op=1;
    while (r[op]==1)
    {
        jll[wz[op]]=(qh[1]+mo)%mo;
        op++;
    }
    for (int j=2;j<=n;j++)
    {
        long long zss=a[j]*2;long long aaa=a[j]*2%mo;
        int kk=1;
        if (a[j]>0)
        {
            while (gs>0 && zs[gs]+zss*mi[k[gs]]>0)
            {
                zss=min(5000000000,zs[gs]+zss*mi[k[gs]]);
                aaa=(aa[gs]+aaa*mi[k[gs]]%mo+mo)%mo;
                kk=kk+k[gs];
                gs--;
            }
            if (gs==0)
            {
                gs++;
            }
            else
            {
                zss=min(5000000000,zs[gs]+zss*mi[k[gs]]);
                aaa=(aa[gs]+aaa*mi[k[gs]]%mo+mo)%mo;
                kk=k[gs]+kk;
            }
            ri[gs]=j;
            k[gs]=kk;
            zs[gs]=zss;
            aa[gs]=aaa;
            qh[gs]=(qh[gs-1]+aaa+mo)%mo;
        }
        else
        {
            gs++;
            le[gs]=j;
            ri[gs]=j;
            zs[gs]=zss;
            k[gs]=kk;
            aa[gs]=aaa;
            qh[gs]=(qh[gs-1]+aaa+mo)%mo;
        }
        change(1,1,n,le[gs],ri[gs],le[gs],ri[gs],k[gs],gs);
        while (op<=m && j==r[op])
        {
            if (l[op]==r[op])
            {
                jll[wz[op]]=a[l[op]];
                op++;
            }
            else
            {
                jr=0;jl=0;jk=0;jg=0;
                if (l[op]>1)
                {
                    find(1,1,n,l[op]-1);
                    jll[wz[op]]=(qh[gs]-qh[jg]+mo)%mo;
                    int opt=0;
                    if (jk==jr-jl+1) opt=1;
                    jll[wz[op]]=(jll[wz[op]]+(zd[jr]-zd[l[op]-1]+mo)%mo*qsm(mi[jr-(jk-(l[op]-jl))+opt],mo-2)%mo)%mo;
                    
                    int pd=jl;
                    jr=0;jl=0;jk=0;jg=0;
                    find(1,1,n,l[op]);
                    if (jl!=pd)
                    {
                        jll[wz[op]]=(jll[wz[op]]-(zd[jr]-zd[jl-1]+mo)%mo*qsm(mi[jr-jk+1],mo-2)%mo)%mo;
                    }
                    op++;
                }
                else
                {
                    jll[wz[op]]=(qh[gs]+mo)%mo;
                    op++;
                }
            }
        }
    }
    for (int i=1;i<=m;i++)
    {
        printf("%l64d\n",(jll[i]+mo)%mo);
    }
}

猜你喜欢

转载自www.cnblogs.com/RainbowCrown/p/11628120.html