zoukankan      html  css  js  c++  java
  • 冷门trick:线性区间单调求和

    冷门trick:线性区间单调求和

    要解决的问题,见 这里 的描述。

    这个trick不太好自己yy,这里直接开讲:我们任取一个 (p),作为分界线,并保证这个 (p) 被包含在当前的区间里面。

    对于 (p) 左边的东西,我们维护一个后缀和,每次直接查询。对于 (p) 右边的东西,我们用一个单调的东西 (cur),每次 (r)​ 增加的时候,一个个加上去。那对于当前区间,答案就是左边的答案+右边的答案。

    有一个问题是,区间不断的右移,如果哪一天 (p) 跑到区间左边了,咋办?

    此时我们把 (p) 移动到当前区间的右端点,重构后缀和。为保证复杂度,我们记录 (p) 上次的位置 (p')​,然后只需求出 ((p',p]) 这一段的后缀和即可。为啥只需要这一部分,因为我们动 (p) ,说明 (l>p'),而 (l) 是递增的,所以更左边的信息我们 再也不需要 了,直接不管。而只有 (p')(p)​ 这一段新增的,需要重构一下后缀和。顺便再令 (cur) 为一个不影响答案的元素。比如加法中的 (0),乘法中的 (1),单位矩阵,或者是求min/max时的(pm infin)

    分析复杂度:

    • 对于 (cur),因为它的位置只会增,所以均摊是 (O(n))
    • 对于 (p),它会动很多次,但是复杂度是每次动的长度(即 (p-p'))的和,发现它也是 (O(n))

    于是总复杂度严格线性。

    对于洛谷上那个我出的板子题,参考代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    namespace Flandre_Scarlet
    {
        #define N   10000007
        #define mod 59861874
        #define int long long
        #define F(i,l,r) for(int i=l;i<=r;++i)
        #define D(i,r,l) for(int i=r;i>=l;--i)
        #define Fs(i,l,r,c) for(int i=l;i<=r;c)
        #define Ds(i,r,l,c) for(int i=r;i>=l;c)
        #define MEM(x,a) memset(x,a,sizeof(x))
        #define FK(x) MEM(x,0)
        #define Tra(i,u) for(int i=G.st(u),v=G.to(i);~i;i=G.nx(i),v=G.to(i))
        #define p_b push_back
        #define sz(a) ((int)a.size())
        #define all(a) a.begin(),a.end()
        #define iter(a,p) (a.begin()+p)
        #define PUT(a,n) F(i,1,n) printf("%d ",a[i]); puts("");
        int I() {char c=getchar(); int x=0; int f=1; while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar(); while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return ((f==1)?x:-x);}
        template <typename T> void Rd(T& arg){arg=I();}
        template <typename T,typename...Types> void Rd(T& arg,Types&...args){arg=I(); Rd(args...);}
        void RA(int *p,int n) {F(i,1,n) *p=I(),++p;}
        int n,q,seed;
        void Input()
        {
            n=I(); q=I(); seed=I();
        }
        int uu=1;
        int rnd() {uu=uu*seed%100003; return (uu<<5)|(uu<<4)|(uu);}
        int rrnd(int l,int r) {return rnd()%(r-l+1)+l;}
        int a[N],ll[N],rr[N];
        void gen()
        {
            for(int i=1;i<=n;++i)
            {
                a[i]=rrnd(1,2000);
            }
            int x=0,y=0;
            for(int i=1;i<=q;++i)
            {
                x=x+rrnd(1,10); y=max(y,x);
                int t=rnd();
                if (t%3) y=y+rrnd(1,50); 
                else y=y+rrnd(1,400);
                x=min(x,n); y=min(y,n); 
                ll[i]=x; rr[i]=y;
                assert(ll[i]>=ll[i-1]); assert(rr[i]>=rr[i-1]);
            }
        }
    
        int suf[N];
        void Sakuya()
        {
            gen();
    
            int las=0,pos=0; // pos:分界点p; las:上一次在哪
            int cur=1,curp=0; // cur:右边那一段的“和”; curp:处理到了哪
            int ans=1,mod2=998244353;
            F(tt,1,q)
            {
                int l=ll[tt],r=rr[tt];
                
                if (l>pos) // l>pos, 此时需要重构后缀和
                {
                    las=pos; pos=r; 
                    cur=1,curp=r; // cur设置为一个无影响元, 这里令它为1, 因为是乘法
                    suf[pos]=a[r]; D(i,pos-1,las+1) suf[i]=suf[i+1]*a[i]%mod;
                    // 只重构 (las,pos] 段的前缀和
                }
                else
                {
                    while(curp<r) ++curp,cur=cur*a[curp]%mod; // 单调的动cur即可
                }
                // 左边的答案就是suf[l], 查询后缀和; 右边的答案是cur, 单调的加上去就行
                int ansi=cur*suf[l]%mod;
                ans=ans*(114514ll*tt%mod2+(1919810^ansi))%mod2; // 神秘输出格式
            }
            printf("%lld
    ",ans);
        }
        void IsMyWife()
        {
            Input();
            Sakuya();
        }
    }
    #undef int //long long
    int main()
    {
        Flandre_Scarlet::IsMyWife();
        getchar();
        return 0;
    }
    
  • 相关阅读:
    Java时间和时间戳的相互转换
    linux 通过pid 寻找程序路径的最简单命令(pwdx)
    Oracle--存储过程学习进阶
    经典sql总结(2)
    经典sql总结(1)
    类的初始化
    StringBuffer和String 的例子
    i=i++
    一个异常学习的好例子
    有空研究这篇http://blog.csdn.net/studyvcmfc/article/details/7720258 研究后写篇记录
  • 原文地址:https://www.cnblogs.com/LightningUZ/p/15115111.html
Copyright © 2011-2022 走看看