Acwing-246. 区间最大公约数
本蒟蒻的第二篇题解qwq.
题目大意:
给定一个长度为
1.C l r d
,表示把
2.Q l r
,表示询问
《普通的解法》:
如果你是一个只会打暴力的小蒟蒻(就像我),看到题目后,是不是就只能无奈的打开 IDE,
打起暴力代码了吗?拜托,那可是暴力啊,数据这么大,你怎么能这么暴力地对待他呢?
有一天,一名蒟蒻打开了这道题:
蒟蒻:这不就是求最大公约数吗,还不简简单单!
然而他完全没有注意到数据范围...
半小时过去了...
蒟蒻:哈,本地样例过了!我也有实力切困难题了!
当提交后,发现TLE时...
蒟蒻:伟大的出题人啊,你为什么要给那么大的数据范围?
数据:那可不是,如果我不出这么大的数据范围,他还佩成为困难题吗?
蒟蒻:www...我因为这道题白白浪费了半个小时,还不去
写水题好...
所以上面这段对话告诉我们不要轻视数据范围qwq
标准解法+证明过程:
在点进这道题之前,先问一句话,知道什么是欧几里德算法吗?
什么,你不知道?那还不快去 这篇网站 补一下.
那么现在,你需要知道一个特性,一个关于欧几里德算法的一个特性:
什么,你说你不相信?那我们不妨来证明一下:
令
-
假设
,则 . -
假设
,那么令 .
-
. -
令
.假设
,那么:
因为
由于
没错,就证明完了.
而根据数学归纳法,我们又能推出下面的式子:
-
. -
看到这个结构是不是很熟悉?没错,这不就是差分吗!
于是这道题就很明显了,维护一个差分数组,区间修改则改为了单点修改,查询就...就什么?
额...好像还是要遍历整个区间吧...
这个时候,就得请出我们的线段树了!!
没错,以查询和修改区间为名,与这道题可谓是天造地设,只需要让
不过,式子中的
这个时候,
这不就退化成了前缀和吗?
好了,废话不多说,上代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct node{
ll l,r;
ll v,d;
}tree[2000005];
char c;
ll a[500005],b[500005],n,m,u,v,w;
inline ll lu(ll u){return u << 1;}//返回左子结点编号
inline ll ru(ll u){return u << 1 | 1;}//返回右子结点编号
ll gcd(ll a,ll b){//gcd函数,采用辗转相除法
if(b == 0) return a;
else return gcd(b,a % b);
}
void push_up(ll u){
tree[u].v = tree[lu(u)].v + tree[ru(u)].v;
tree[u].d = gcd(tree[lu(u)].d,tree[ru(u)].d);
}
void build(ll u,ll l,ll r){//建树
tree[u].l = l,tree[u].r = r;
if(l == r){
tree[u].v = b[l],tree[u].d = b[l];
return;
}else{
ll mid = (l + r) >> 1;
build(lu(u),l,mid);
build(ru(u),mid + 1,r);
push_up(u);
}
}
void update(ll u,ll f,ll w){
if(tree[u].l == f && tree[u].r == f){
tree[u].d += w,tree[u].v += w;return;
}else{
ll mid = (tree[u].l + tree[u].r) >> 1;
if(f <= mid) update(lu(u),f,w);
if(f > mid) update(ru(u),f,w);
push_up(u);
return;
}
}
ll query(ll u,ll ml,ll mr){
if(tree[u].l >= ml && mr >= tree[u].r) return abs(tree[u].d);
ll mid = (tree[u].l + tree[u].r) >> 1,res = 0;
if(ml <= mid) res = gcd(res,query(lu(u),ml,mr));
if(mr > mid) res = gcd(res,query(ru(u),ml,mr));
return res;
}
ll query2(ll u,ll ml,ll mr){ // 求前缀和
if(tree[u].l >= ml && tree[u].r <= mr) return tree[u].v;
ll mid = (tree[u].l + tree[u].r) >> 1,res = 0;
if(ml <= mid) res += query2(lu(u),ml,mr);
if(mr > mid) res += query2(ru(u),ml,mr);
return res;
}
int main(){
ios::sync_with_stdio(false);//加快读入速度
cin >> n >> m;
for(ll i = 1;i <= n;i++){
cin >> a[i];
b[i] = a[i] - a[i - 1];//b为差分数组
}
build(1,1,n);
while(m--){
cin >> c;
if(c == 'C'){
cin >> u >> v >> w;
update(1,u,w);
if(v + 1 <= n) update(1,v + 1,w * -1);//防止单点修改时越界
}else{
cin >> u >> v;
if(u == v){
cout<<query2(1,1,u)<<'\n';//当区间只有一个元素时,答案为al
}else{
ll tmp = abs(query2(1,1,u));
cout<<gcd(tmp,query(1,u + 1,v))<<'\n';
}
}
}
return 0;
}
这次写的有些急,有问题还请多多指出,谢谢!qwq
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架