zoukankan      html  css  js  c++  java
  • 【BZOJ1938】[CROATIAN2010] ALADIN(线段树+类欧几里得算法)

    点此看题面

    大致题意: 给定一个长度为(n)的序列,支持两种操作:给定(L,R,A,B),把第(x)个位置((x∈[L,R]))上的数修改为(((x-L+1) imes A)\%B);给定(L,R),询问区间([L,R])中数的和。

    前言

    感觉类欧几里得算法知道基本套路之后似乎并不是很难?坐等打脸。

    反正这道题是我自己推出来的(尽管调试花了很久)。

    线段树

    这种区间修改、区间查询的问题,一看就是一个简单的线段树板子+毒瘤的数学转化。

    而这道题中用到的毒瘤数学知识,就是类欧几里得算法

    考虑线段树上每个点我们需要维护一段区间的和,也就是说,我们需要快速计算(sum_{i=l}^r(iA\%B))

    一个显然的差分,若设(G(n,A,B)=sum_{i=1}^n(iA\%B)),则原式等同于(G(r,A,B)-G(l-1,A,B)),因此只要考虑如何计算(f)就可以了。

    而众所周知,取模有一个套路的转化:

    [G(n,A,B)=sum_{i=1}^n(iA-Blfloorfrac{iA}B floor)=Afrac{n(n+1)}2-Bsum_{i=1}^nlfloorfrac{iA}B floor ]

    在这个式子中,我们只需着重关注(f(n,A,B)=sum_{i=1}^nlfloorfrac{iA}B floor)这一项,因为其他部分都是可以直接(O(1))得到的。

    而如何求解这一项,就需要类欧几里得算法了。

    类欧几里得算法

    对于(f(n,A,B)),套路地进行分类讨论:

    如果(Age B),我们提取出(lfloorfrac AB floor)的整数部分,得到:

    [egin{align}f(n,A,B)&=(sum_{i=1}^ni imes lfloorfrac AB floor+lfloorfrac{i(A\%B)}B floor)\&=frac{n(n+1)}2 imeslfloorfrac AB floor+f(n,A\%B,B)end{align} ]

    也就是说,这一类情况可以转化为(A<B)的情况做。

    如果(A<B),我们看图:(其实这张图和【BZOJ3817】Sum是一样的)

    (y=frac AB x)(如直线(OA)所示),那么(f(n,A,B))的意义就是求(x∈[1,n])时在(y)下方的整点个数(即(△AOM)中整点的个数)。

    然后我们作出(y)的反函数(y'=frac BA x)别问为什么这么做,问就是套路),那么(△AOM)中整点的个数就变成了(△BON)中整点的个数。

    (p=lfloorfrac AB n floor),显然(OC'=p)。且由于整点横坐标必然为整数,所以矩形(BCC'B')中不可能有整点。

    然后我们再容斥,就变成了矩形(B'C'ON)中整点的个数(-△C'OD)中整点的个数(+)线段(OD)上整点的个数。

    其中,矩形(B'C'ON)中整点的个数就是(n imes p)(△C'OD)中整点的个数可以套用(f)函数变成(f(p,B,A))。至于线段(OD)上整点的个数,我们可以在最初就把(A,B)同时约去它们的(gcd)(显然这不改变斜率,也就不改变答案),则个数显然是(lfloorfrac pA floor)

    可以发现(A,B)的变化方式和求(gcd)时是一样的,因此复杂度也同样是(O(logn))。(这就是这个算法被称作类欧几里得算法的唯一原因)

    一个有趣的小技巧

    一个调试了一个多小时之后发现的小技巧。

    在这道题中,很容易就会爆(long long)

    但不要慌,爆(long long)的结果在模(2^{63})意义下值是相等的。我们完全可以放任它去随意爆(long long),只要在最后输出答案前给小于(0)的答案加上(2^{63})即可.

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define LL long long
    #define M 50000
    using namespace std;
    int n,dc,dv[2*M+5],op[M+5],l[M+5],r[M+5],x[M+5],y[M+5];
    I int gcd(CI x,CI y) {return y?gcd(y,x%y):x;}
    I LL Get(CI n,RI A,RI B)//类欧几里得算法
    {
    	if(!n||!A) return 0;if(A>=B) return 1LL*A/B*n*(n+1)/2+Get(n,A%B,B);//若A≥B,转化为A<B
    	RI p=1LL*n*A/B;return 1LL*n*p-Get(p,B,A)+p/A;//容斥计算答案
    }
    I LL GV(CI n,CI A,CI B)//求值
    {
    	RI g=gcd(A,B);return 1LL*A*n*(n+1)/2-1LL*B*Get(n,A/g,B/g);//计算,注意把A,B约去gcd
    }
    class SegmentTree
    {
    	private:
    		#define PT CI l=1,CI r=dc,CI rt=1
    		#define LT l,mid,rt<<1
    		#define RT mid+1,r,rt<<1|1
    		#define PU(x) (O[x].V=O[x<<1].V+O[x<<1|1].V)
    		#define PD(x) O[x].B&&(T(x<<1,l,mid,O[x].A,O[x].B,O[x].K),
    			T(x<<1|1,mid+1,r,O[x].A,O[x].B,O[x].K),O[x].A=O[x].B=O[x].K=0)
    		#define T(x,l,r,a,b,k) (O[x].A=a,O[x].B=b,
    			O[x].K=k,O[x].V=GV(dv[r]-(k),a,b)-GV(dv[(l)-1]-(k),a,b))
    		struct node {int A,B,K,S[2];LL V;}O[M<<4];
    	public:
    		I void U(CI L,CI R,CI A,CI B,PT)//区间赋值
    		{
    			if(L<=dv[l-1]+1&&dv[r]<=R) return (void)T(rt,l,r,A,B,L-1);int mid=l+r>>1;PD(rt),
    			L<=dv[mid]&&(U(L,R,A,B,LT),0),R>dv[mid]&&(U(L,R,A,B,RT),0),PU(rt);
    		}
    		I LL Q(CI L,CI R,PT)//询问
    		{
    			if(L<=dv[l-1]+1&&dv[r]<=R) return O[rt].V;int mid=l+r>>1;PD(rt);
    			return (L<=dv[mid]?Q(L,R,LT):0)+(R>dv[mid]?Q(L,R,RT):0);
    		}
    }S;
    int main()
    {
    	RI Qt,i;for(scanf("%d%d",&n,&Qt),i=1;i<=Qt;++i) scanf("%d%d%d",op+i,l+i,r+i),
    		op[i]^2&&(scanf("%d%d",x+i,y+i),x[i]%=y[i]),dv[++dc]=l[i]-1,dv[++dc]=r[i];//存下操作以离散化
    	sort(dv+1,dv+dc+1),dc=unique(dv+1,dv+dc+1)-dv-1;LL t;
    	for(i=1;i<=Qt;++i) op[i]^2?S.U(l[i],r[i],x[i],y[i]),0//修改
    		:(t=S.Q(l[i],r[i]),printf("%lld
    ",t<0?t+(1ull<<63):t));return 0;//询问,对于小于0的答案加上2^63
    }
    
  • 相关阅读:
    md测试
    安利好用的小工具及软件(常更新)
    08-无人值守自动批量安装系统(下)
    建立本地yum仓库
    如何知道命令是由哪个服务提供的
    08-无人值守自动批量安装系统(上)
    07-软件管理
    06-时间管理
    05-用户和组以及权限
    04-编辑文本
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1938.html
Copyright © 2011-2022 走看看