1 条题解

  • 0
    @ 2025-8-24 22:00:07

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 浅色调
    **

    搬运于2025-08-24 22:00:07,当前版本为作者最后更新于2018-09-08 08:50:28,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    Solution:

    本题黑的不行啊,两天就荒(废)在这题上了!

    思路:数学大套路+线段树。

    题目中唯一出现的数学式子:i=1in(iAmodB)\sum\limits_{i=1}^{i\leq n} {(i*A\mod B)},那么切入点当然是如何快速求该式子咯。

    我们对式子变形:原式$=A*\sum\limits_{i=1}^{i\leq n}{i}-B*\sum\limits_{i=1}^{i\leq n}{\lfloor \frac{i*A}{B} \rfloor}$。

    被减数式子很好算直接忽略,减数式子的解决关键是式子$\sum\limits_{i=1}^{i\leq n}{\lfloor \frac{i*A}{B} \rfloor}$,对此式子我们分情况讨论(A==BA==B的情况该式子为0,直接忽略咯):

    \quad\quad\quad 1、A>BA>B,我们假设A=kB+rA=kB+r,则原式子化为$\sum\limits_{i=1}^{i\leq n}{\lfloor \frac{i*(kB+r)}{B} \rfloor}=k*\sum\limits_{i=1}^{i\leq n}{i}+\sum\limits_{i=1}^{i\leq n}{\lfloor \frac{i*r}{B} \rfloor}$,我们把rr当作新的AA,那么就将该式子转化为了A<BA<B的情况,于是关键就成了A<BA<B时如何快速求原式子。

    \quad\quad\quad 2、A<BA<B,我们将其抽象到平面直角坐标系上,不难发现$\sum\limits_{i=1}^{i\leq n}{\lfloor \frac{i*A}{B} \rfloor}$实际求的是坐标为(0,0),(n,0),(n,nAB)(0,0),(n,0),(n,\frac{n*A}{B})三点围成的三角形的不在XX轴上的格点个数,可能有点难以理解,我们画图理解(留图待画、手绘勿喷):

    如图,对角线上每个被标记的点到x轴的垂线段上的格点(除开x轴的格点),所对应的就是每个iAB\lfloor \frac{i*A}{B} \rfloor。我们若直接算下三角的格点个数会很麻烦,但是很容易算出整个矩形的格点个数,我们设m=nABm=\lfloor \frac{n*A}{B} \rfloor,则矩形的格点个数为nmn*m,我们用矩形的格点个数-上三角的格点个数+对角线上的格点个数,就能得到原式子的值。如何求上三角的格点个数和对角线的格点个数呢?我们把上三角逆时针旋转90度,就能得到一个类似于下三角的一条边为整数的三角形,用同样的方法去求,发现上三角的格点个数恰好等于$\sum\limits_{i=1}^{i\leq m}{\lfloor \frac{i*B}{A} \rfloor}$,因为A<BA<B,我们又回到了第1种A>BA>B情况,于是可以递归去求(递归边界就是ABA|B返回0)。而对角线斜率为Agcd(A,B)Bgcd(A,B)\frac{\frac{A}{gcd(A,B)}}{\frac{B}{gcd(A,B)}},那么横坐标每隔Bgcd(A,B)\frac{B}{gcd(A,B)}个单位会有一个格点出现,所以对角线上共有ngcd(A,B)B\frac{n*gcd(A,B)}{B}个格点。不难发现整个递归过程就是个类欧几里得的求法,时间复杂度为O(logn)O(\log n)

    有了上面的结论,我们就能在O(logn)O(\log n)的复杂度下修改一段区间,那么对于原题的区间查询,我们使用懒惰标记,记录每段被修改的A,BA,B和前一个点位置stst,然后任意一度区间[l,r][l,r]的和都可以用sum[r]sum[l1]sum[r]-sum[l-1]去算,而每个sum[i]sum[i]直接调用上面的递归过程就好了。

    细节太多,注意:区间肯定得离散,而求区间和时用到了前缀和的思想,一个简单的离散方法是对询问的l,rl,r,将l1,rl-1,r离散,然后线段树建树时每个节点维护的是一整段区间,要把每段小的区间都表示出来(开始30分的原因)。

    最后总时间复杂度O(qlog2n)O(q\log^2 n),稳妥!(>.^_^.<咕咕)

        \quad\;\;欢迎来踩博客five20(蒟蒻写题解不易,转载请注明出处,万分感谢~!)

    代码:

        /*Code by 520 -- 9.7*/
        #include<bits/stdc++.h>
        #define il inline
        #define ll long long
        #define RE register
        #define For(i,a,b) for(RE int (i)=(a);(i)<=(b);++(i))
        #define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);--(i))
        #define lson l,m,rt<<1
        #define rson m,r,rt<<1|1
        using namespace std;
        const int N=1000005;
        int n,m,flag[N],L[N],R[N],*Q[N],cnt,tot,val[N];
        ll A[N],B[N];
        struct node{
            ll sum,a,b,st,len;
        }t[N];
        int gi(){
            int a=0;char x=getchar();
            while(x<'0'||x>'9')x=getchar();
            while(x>='0'&&x<='9')a=(a<<3)+(a<<1)+(x^48),x=getchar();
            return a;
        }
        il bool cmp(int *a,int *b){return *a<*b;}
        ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
        ll calc(ll a,ll b,ll n){
            ll x=a/b;a%=b;
            ll sum=n*(n+1)/2*x;
            if(!a||!b) return sum;
            ll lala=n/b,m=a*n/b;
            return sum+n*m-calc(b,a,m)+lala;
        }
        il ll solve(ll a,ll b,ll n){
            if(n<1)return 0;
            ll g=gcd(a,b);
            return n*(n+1)/2*a-b*calc(a/g,b/g,n);
        }
        il void pushup(int rt){t[rt].sum=t[rt<<1].sum+t[rt<<1|1].sum;}
        il void gai(ll A,ll B,ll st,int rt){
            t[rt].a=A,t[rt].b=B,t[rt].st=st;
            t[rt].sum=solve(A,B,st+t[rt].len-1)-solve(A,B,st-1);
        }    
        il void pushdown(int rt){
            if(t[rt].b){
                gai(t[rt].a,t[rt].b,t[rt].st,rt<<1);
                gai(t[rt].a,t[rt].b,t[rt].st+t[rt<<1].len,rt<<1|1);
                t[rt].b=0;
            }
        }
        void build(int l,int r,int rt){
            if(l+1==r){t[rt]=node{0,0,0,0,val[r]-val[l]};return;}
            int m=l+r>>1;
            t[rt]=node{0,0,0,0,val[r]-val[l]};
            build(lson),build(rson);
        }
        void update(ll A,ll B,int L,int R,int l,int r,int rt){
            if(R<=l||r<=L)return;
            if(L<=l&&R>=r){gai(A,B,val[l]-val[L]+1,rt);return;}
            pushdown(rt);
            int m=l+r>>1;
            if(L<=m) update(A,B,L,R,lson);
            if(R>=m) update(A,B,L,R,rson);
            pushup(rt);
        }
        ll query(int L,int R,int l,int r,int rt){
            if(R<=l||r<=L)return 0;
            if(L<=l&&R>=r)return t[rt].sum;
            pushdown(rt);
            int m=l+r>>1;
            ll ret=0;
            if(L<=m) ret+=query(L,R,lson);
            if(R>=m) ret+=query(L,R,rson);
            return ret;
        }
        int main(){
            n=gi(),m=gi();
            For(i,1,m) {
                flag[i]=gi(),L[i]=gi()-1,R[i]=gi(),Q[++tot]=&L[i],Q[++tot]=&R[i];
                if(flag[i]==1) A[i]=gi(),B[i]=gi();
            }
            sort(Q+1,Q+tot+1,cmp);
            int lst=-1;
            For(i,1,tot) if(*Q[i]!=lst) lst=*Q[i],*Q[i]=++cnt,val[cnt]=lst;else *Q[i]=cnt;
            build(1,cnt,1);
            For(i,1,m)
                if(flag[i]==1) update(A[i],B[i],L[i],R[i],1,cnt,1);
                else printf("%lld\n",query(L[i],R[i],1,cnt,1));
            return 0;
        }    
    
    • 1

    信息

    ID
    3406
    时间
    8000ms
    内存
    64MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者