【LuoguP4433】[COCI2009-2010#1] ALADIN(含类欧几里得算法推导)

开发者福利!热门AI工具限时免费用 购周边即赠Coding Plan Lite,Claude Code、Cursor等20+工具畅享,效率翻倍! 阅读详情

题目链接

题意简述

区间赋值模意义下等差数列,询问区间和
N ≤ 1 0 9 , Q ≤ 1 0 5 N\leq 10^9,Q\leq 10^5 N109,Q105

Sol

每次操作就是把操作区间 [ L , R ] [L,R] [L,R]中的数赋值成:
( X − L + 1 ) ∗ A   m o d   B (X-L+1)*A\ mod\ B (XL+1)A mod B
考虑用线段树维护。
我们只需要能快速知道一段区间 [ l , r ] [l,r] [l,r]被覆盖后的和就行了,因为覆盖的标记易于下传:
∑ i = l r ( i − L + 1 ) ∗ A   m o d   B \sum_{i=l}^{r} (i-L+1)*A\ mod\ B i=lr(iL+1)A mod B
根据基础的数学知识,mod显然不好算,把它拆开:
∑ i = l r ( i − L + 1 ) ∗ A − ⌊ ( i − L + 1 ) ∗ A B ⌋ ∗ B \sum_{i=l}^r (i-L+1)*A-\bigg\lfloor \frac{(i-L+1)*A}{B}\bigg\rfloor*B i=lr(iL+1)AB(iL+1)AB
前面那一坨直接算就行了,关键是后面这一坨。我们可以把后面的式子写简洁一些:
B ∗ ∑ i = q p ⌊ A ∗ i B ⌋ B*\sum_{i=q}^{p} \bigg\lfloor \frac{A*i}{B}\bigg\rfloor Bi=qpBAi

其中 p = l − L + 1 , q = r − l + 1 p=l-L+1,q=r-l+1 p=lL+1,q=rl+1, B B B乘在外面不用管,而 p p p q q q的求和可以拆分为 0 ∼ q 0\sim q 0q 的和减去 0 ∼ p − 1 0\sim p-1 0p1的和,这样我们只需要会算以下式子:
∑ i = 0 n ⌊ A ∗ i B ⌋ \sum_{i=0}^n \bigg\lfloor \frac{A*i}{B}\bigg\rfloor i=0nBAi

这玩意就可以用类欧几里得算法求,具体过程如下。


第一种方法是用几何的思想直观的来理解,但是似乎不是很好推出明确的式子,所以这里主要用代数推导。
对于式子:
∑ i = 0 n ⌊ A ∗ i + B C ⌋ \sum_{i=0}^n \bigg\lfloor \frac{A*i+B}{C}\bigg\rfloor i=0nCAi+B
其几何意义就是直线 y = A x + B C y=\dfrac{Ax+B}{C} y=CAx+B 下方和x轴与y轴与直线 x = n x=n x=n围成的图形中包含的纵坐标不为0的整点个数,这个画个图就很好理解:
令   F ( n , A , B , C ) = ∑ i = 0 n ⌊ A ∗ i + B C ⌋ 令\ F(n,A,B,C)=\sum_{i=0}^n \bigg\lfloor \frac{A*i+B}{C}\bigg\rfloor  F(n,A,B,C)=i=0nCAi+B
如果 A , B A,B A,B不小于 C C C,那么可以把整除的部分提出来直接计算,这个很简单,所以只讨论 A , B A,B A,B都小于 C C C的情况。

m = ⌊ A ∗ n + B C ⌋ m=\big\lfloor \frac{A*n+B}{C}\big\rfloor m=CAn+B,通过几何意义转换(这里方便推式子j从0到m-1):
F ( n , A , B , C ) = ∑ i = 0 n ∑ j = 0 m − 1 [ ( A ∗ i + B ) ≥ C ∗ ( j + 1 ) ] F(n,A,B,C)=\sum_{i=0}^n\sum_{j=0}^{m-1} \bigg[(A*i+B) \geq C*(j+1)\bigg] F(n,A,B,C)=i=0nj=0m1[(Ai+B)C(j+1)]
调换求和顺序
F ( n , A , B , C ) = ∑ i = 0 m − 1 ∑ j = 0 n [ ( A ∗ j + B ) ≥ C ∗ ( i + 1 ) ] F(n,A,B,C)=\sum_{i=0}^{m-1}\sum_{j=0}^n\bigg [(A*j+B)\geq C*(i+1)\bigg] F(n,A,B,C)=i=0m1j=0n[(Aj+B)C(i+1)]

移项变形,减1去掉不等式的等号:
F ( n , A , B , C ) = ∑ i = 0 m − 1 ∑ j = 0 n [ ( C ∗ i + C − B − 1 ) &lt; A ∗ j ] F(n,A,B,C)=\sum_{i=0}^{m-1}\sum_{j=0}^n \bigg[(C*i+C-B-1) &lt; A*j\bigg] F(n,A,B,C)=i=0m1j=0n[(Ci+CB1)<Aj]
接下来容斥一下,用总数减去不合法的情况:
F ( n , A , B , C ) = ∑ i = 0 m − 1 ( n + 1 − ∑ j = 0 n [ ( C ∗ i + C − B − 1 ) ≥ A ∗ j ] ) F(n,A,B,C)=\sum_{i=0}^{m-1}(n+1-\sum_{j=0}^n \bigg[(C*i+C-B-1) \geq A*j\bigg]) F(n,A,B,C)=i=0m1(n+1j=0n[(Ci+CB1)Aj])

这一下里面的不就和原来我们的 F ( x ) F(x) F(x)的形式差不多了嘛。
考虑到我们之前 j j j是从 0 ∼ m − 1 0\sim m-1 0m1而当 j = 0 j=0 j=0的时候,左边必大于0,而右边为0,贡献一定存在,故可以把原式改写,先把1的贡献减在外面:
F ( n , A , B , C ) = ∑ i = 0 m − 1 ( n − ∑ j = 0 n − 1 [ ( C ∗ i + C − B − 1 ) ≥ A ∗ ( j + 1 ) ] ) F(n,A,B,C)=\sum_{i=0}^{m-1}(n-\sum_{j=0}^{n-1} \bigg[(C*i+C-B-1) \geq A*(j+1)\bigg]) F(n,A,B,C)=i=0m1(nj=0n1[(Ci+CB1)A(j+1)])
这样不就提出 n n n来:
F ( n , A , B , C ) = n ∗ m − ∑ i = 0 m − 1 ∑ j = 0 n − 1 [ ( C ∗ i + C − B − 1 ) ≥ A ∗ ( j + 1 ) ] F(n,A,B,C)=n*m-\sum_{i=0}^{m-1}\sum_{j=0}^{n-1} \bigg[(C*i+C-B-1) \geq A*(j+1)\bigg] F(n,A,B,C)=nmi=0m1j=0n1[(Ci+CB1)A(j+1)]
后面的式子很先然可以根据 F ( x ) F(x) F(x)的定义改写:
F ( n , A , B , C ) = n ∗ m − F ( m − 1 , C , C − B − 1 , A ) F(n,A,B,C)=n*m-F(m-1,C,C-B-1,A) F(n,A,B,C)=nmF(m1,C,CB1,A)

这样递归下去不停计算,显然的是当最后 A A A变成 0 0 0的时候贡献为 0 0 0
只用观察 A A A C C C,发现他们调换了位置,这样必然使得下一次的 C ≤ A C \leq A CA,那么可以先把这部分提出来计算,实际上就是: ( A , C ) → ( C % A , A ) (A,C) \rightarrow (C\%A,A) (A,C)(C%A,A),这不是就是和 g c d gcd gcd长的一样吗,所以复杂度也是 O ( l o g n ) O(logn) O(logn)

其实类欧主要是一种把整除转化为求整点的思想

直接写类欧的代码差不多长这样:

ll likegcd(ll n,ll a,ll b,ll c){
	if(c<=a||c<=b) return (a/c*n%mod*(n+1)%mod*inv2%mod+b/c*(n+1)%mod+likegcd(n,a%c,b%c,c))%mod;
	if(!a||!n) return 0;
	ll m=(a*n+b)/c;
	return (n*m%mod-likegcd(m-1,c,c-b-1,a)+mod)%mod;
}

所以这道题也差不多做完了
但是有两个要注意的地方:
1.直接乘可能会爆 l o n g l o n g long long longlong,所以 _ _ i n t 128 \_\_int128 __int128大法好
2.本题卡空间所以要对操作区间和询问区间离散化,注意离散化的是 l − 1 和 r l-1和r l1r

代码:

#include<bits/stdc++.h>
using namespace std;
#define Set(a,b) memset(a,b,sizeof(a))
template<class T>inline void init(T&x){
	x=0;char ch=getchar();bool t=0;
	for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') t=1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
	if(t) x=-x;return;
}
int n,q;
//#define ll __int128
typedef long long ll;
const int MAXN=2e6+1;
const int M=5e4+1;
ll sum[MAXN];
int L[MAXN],R[MAXN];
int tagA[MAXN],tagB[MAXN],tagL[MAXN];
struct Query{
	int l,r,A,B;
}Q[M];
int stk[M<<1];int top=0;
#define ls (u<<1)
#define rs (u<<1|1)
void build(int u,int l,int r){
	if(l==r) {L[u]=stk[l-1]+1,R[u]=stk[r];return;}
	int mid=l+r>>1;
	build(ls,l,mid);build(rs,mid+1,r);
	L[u]=L[ls],R[u]=R[rs];return;
}
ll likegcd(ll n,ll a,ll b,ll c){
	if(c<=a||c<=b) return (a/c*(n*(n+1)/2)+b/c*(n+1)+likegcd(n,a%c,b%c,c));
	if(!a||!n) return 0;
	ll m=(a*n+b)/c;
	return n*m-likegcd(m-1,c,c-b-1,a);
}
inline void Cover(int u,int l,int r,int L,int A,int B){
	int len=r-l+1;
	sum[u]=1ll*(l+r-(L<<1)+2)*len/2*A-1ll*B*(likegcd(r-L+1,A,0,B)-likegcd(l-L,A,0,B));
	tagA[u]=A,tagB[u]=B;tagL[u]=L;
	return;
}
inline void push_down(int u){
	if(tagL[u]) {
		int lson=ls,rson=rs;
		Cover(lson,L[lson],R[lson],tagL[u],tagA[u],tagB[u]);
		Cover(rson,L[rson],R[rson],tagL[u],tagA[u],tagB[u]);
		tagL[u]=tagA[u]=tagB[u]=0;
	}
	return;
}
void Modify(int u,int l,int r,int NL,int NR,int LS,int A,int B){
	if(l>=NL&&r<=NR) return Cover(u,L[u],R[u],LS,A,B);
	int lson=ls,rson=rs;push_down(u);
	int mid=l+r>>1;
	if(mid>=NL) Modify(lson,l,mid,NL,NR,LS,A,B);
	if(mid< NR) Modify(rson,mid+1,r,NL,NR,LS,A,B);
	sum[u]=sum[lson]+sum[rson];
	return;
}
ll query(int u,int l,int r,int L,int R){
	if(l>=L&&r<=R) return sum[u];
	push_down(u);
	int mid=l+r>>1;
	if(mid>=R) return query(ls,l,mid,L,R);
	if(mid< L) return query(rs,mid+1,r,L,R);
	return query(ls,l,mid,L,mid)+query(rs,mid+1,r,mid+1,R);
}
int main()
{
	init(n);init(q);
	for(int i=1;i<=q;++i){//区间的离散化是把 l-1 和 r 离散
		int op;init(op);
		if(op==1){
			int l,r,A,B;
			init(l);init(r);init(A);init(B);
			Q[i].l=l,Q[i].r=r,Q[i].A=A,Q[i].B=B;
			stk[++top]=l-1,stk[++top]=r;
		}
		else {
			int l,r;init(l);init(r);
			Q[i].l=l,Q[i].r=r;
			stk[++top]=l-1,stk[++top]=r;
			Q[i].A=-1;
		}
	}
	stk[0]=0;
	sort(stk+1,stk+1+top);top=unique(stk+1,stk+1+top)-stk-1;
	build(1,1,top);
	for(int i=1;i<=q;++i){
		if(Q[i].A!=-1){
			int l=Q[i].l,r=Q[i].r,A=Q[i].A,B=Q[i].B;
			int Li=lower_bound(stk+1,stk+1+top,l-1)-stk+1;
			int Ri=lower_bound(stk+1,stk+1+top,r)-stk;
			Modify(1,1,top,Li,Ri,l,A,B);
		}
		else printf("%lld\n",query(1,1,top,lower_bound(stk+1,stk+1+top,Q[i].l-1)-stk+1,lower_bound(stk+1,stk+1+top,Q[i].r)-stk));
	}
}

CANopen实战:5分钟搞定伺服电机同步控制(附CSP模式配置步骤) 本文详细介绍了如何利用CANopen协议的周期性同步位置模式快速实现伺服电机同步控制。通过解析CSP模式的核心原理,并提供从硬件配置、对象字典设置到PDO映射的完整步骤,帮助工程师在5分钟内构建稳定可靠的多轴同步系统,大幅提升工业自动化设备的控制精度与效率。 阅读详情

相关推荐

原根&离散对数简单总结

原根 1.定义: 定义Ordm(a)Ordm(a)Ord_m(a)为使得ad≡1(modm)ad≡1(modm)a^d\equiv1\;(mod\;m)成立的最小的d(其中a和m互质) 称之为a模m的阶。 阶的性质:Ordm(a)(m)Ordm(a)(m)Ord_m(a)|\Phi(m) 由欧拉定理可知: Ordm(a)≤Φ(m)Ordm(a)≤Φ(m)Ord_m(a)\l...

Yu Gi Oh! 2348

格路问题---算法分析之动态规划

MFC文档编程,可视化实现格路问题,即寻找从始点到终点的最短路径的问题,可视化显示网格以及网格的距离数值,可以设定网格大小以及对每个点的信息右键进行查看和修改。 动态规划方法。 C++6.0编写

【华为OD机试真题 Java语言】66、新工号中数字的最短长度 | 机试真题+思路参考+代码解析

3020年, 空间通信集团的员工人数突破20亿人,即将遇到现有工号不够用的窘境。 >  现在,请你负责调研新工号系统。继承历史传统,新的工号系统由小写英文字母(a-z) 和数字(0-9) 两部分构成。   新工号由一段英文字母开头,之后跟随一 段数字,比如"aaahw0001","a12345","abcd1","a00"。   注意新工号不能全为字母或者数字,允许数字部分有前导0或者全为0。   但是过长的工号会增加同事们的记忆成本,现在给出新工号至少需要分配的人数

KJ.JK 1132

克罗地亚信息学奥赛COCI2009/2010 contest #1

克罗地亚信息学奥赛COCI2009/2010 contest #1 PDF版

洛谷 P7695 [COCI2009-2010#4] PLANINA

洛谷 P7695

阿巴阿巴 378

Luogu4433:[COCI2009-2010#1] ALADIN(欧几里德算法)

先套用一个线段树维护离散化之后的区间的每一段的答案 那么只要考虑怎么下面的东西即可 \[\sum_{i=1}^{n}(A\times i \ mod \ B)\] 拆开就是 \[\sum_{i=1}^{n}A\times i-B\times \sum_{i=1}^{n}\lfloor\frac{A\times i}{B}\rfloor\] 只要考虑计算 \(\sum_{i=1}^{n}\lfloo...

weixin_34365417的博客 244

[COCI2009-2010#1] ALADIN

不过观察图片可以发现翻折后不仅需要计算的点的位置发生改变,而且下底还不是整数,使得上面的方法依然不能用,这时候就需要用到补集转化的思想,也就是求出所有点再减去不合法的点,不过因为斜边上的点也是在答案中的,所以还要加上被减掉的斜边上的点。再加上对角线上的点就能得到答案,但是事实并非如此,因为三角形的高是一个小数,用上面方法求解时候只用了整数部分,而且点的分布并不是中心对称的,以。其中实线为正确的分界,可以发现两边的点分布不均匀,虚线为错误方法认为的分界,与正确分界的答案不一样,所以上面的方法并不正确。

hdxrie的博客 1118

Floyd最短路径算法

     在图论中经常会遇到这样的问题,在一个有向图里,求出任意两个节点之间的最短距离。我们在离散数学、数据结构课上都遇到过这个问题,在计算机网络里介绍网络层的时候好像也遇到过这个问题,记不请了... 但是书本上一律采取的是Dijkstra算法,通过Dijkstra算法可以求出单源最短路径,然后逐个节点利用Dijkstra算法就可以了。不过在这里想换换口味,采取Robert Floyd提出的算法来...

Aladdin的思考 280

P4433 [COCI2009-2010#1] ALADIN

题目描述 给你 n 个盒子,有 q 个操作,操作有两种: 第一种操作输入格式为"1 L R A B",表示将编号为L到R的盒子里的石头数量变为(X−L+1)×A mod B,其中 X为盒子的编号。 第二种操作输入格式为"2 L R",表示查询编号为L到R的盒子里的石头总数。 输入输出格式 输入格式: 第一行有两个数字n(1≤N≤109),q(1≤Q≤5000...

weixin_30706507的博客 248

洛谷 P5170 【模板】欧几里得算法(三经典欧式子模板)

#include<iostream> #include<stdio.h> using namespace std; const int maxn = 1e6 + 10; typedef long long ll; const long long mod = 998244353; const long long inv2 = 499122177; const long l...

qq_41997978的博客 304

COCI2013/2014#ROUND1完整解题报告

COCI2013/2014#ROUND1完整解题报告

(■-■) 1756

欧几里得学习小计

现在才会欧我好菜啊,而且是简单的部分 特殊欧 求L≤vx%M≤RL\leq vx\%M\leq RL≤vx%M≤R的最小的xxx。 记f(M,v,L,R)f(M,v,L,R)f(M,v,L,R)为上面的答案。 按照一般的套路,将特殊的情况判掉,最后再缩减范围即可。 首先保证0≤L≤R<M0\le L\le R<M0≤L≤R<M。 然后判断无解的情况 因为所有vx%Mvx\%Mvx%M一定可以表示成k∗gcd(M,v)k*gcd(M,v)k∗gcd(M,v),所以要

Deep_Thinking的博客 224

欧几里得算法推导

一般形式f(a,b,c,n)=∑ni=0⌊ai+bc⌋f(a,b,c,n)=\sum_{i=0}^n \lfloor \frac{ai+b}{c}\rfloor 给定a,b,c,n,求f(a,b,c,n) 扩展: g(a,b,c,n)=∑ni=0i⌊ai+bc⌋g(a,b,c,n)=\sum_{i=0}^n i\lfloor \frac{ai+b}{c}\rfloor h(a,b,c,n)=

WorldWide_D的博客 4788

Luogu 4433 [COCI2009-2010#1] ALADIN

LOJ 2958 线段树 +欧 主要矛盾就是$\sum\limits_{i = l}^{r}Ai \mod B$怎么做。 拆成前缀和相减的形式,有 $$\sum_{i = 0}^{r}(Ai \mod B) - \sum_{i = 0}^{l - 1}(Ai \mod B)$$ 我们知道 $$a \mod b = a - \left \lfloor \frac{a}{b}...

dashu497731727的博客 244
上一篇: 多项式各种算法学习笔记
下一篇: 【清华集训2014】Sum
NeosKnight
博客等级 码龄9年 21粉丝 151原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值