例题 :CF2050F Maximum modulo equality
给定一个长度为 的序列 ,有 个询问,每次询问给定一个区间 ,让你找一个最大的 ,使得区间内所有的 相同,可以证明一定存在这样一个 ()。
看着很头痛,因为完全不知道从哪里入手,所以只做了这样一个转化 ,然后就不知道该怎么办了。
假设这样一个 满足题意的话,那么所有的 其实都必须可以写成这样的形式,也就是 ,变量的个数仍然比较多,还是没有任何头绪啊。
这时候你会发现这实际上是一个线性方程组,从线性代数的角度思考,实际上任意两个非齐次方程相减就可以得到一个齐次方程,虽然和这个地方差别还是很大,不过可以尝试去沿袭这个思路。
比如对于 来说,把两个等式相减,有 ,这就意味着 一定是 的因子。那么我们只需要保证 内的 对都满足这个条件即可,也就是求这 对的 GCD,但是这会超时,仔细思考, 对是不是太多了?实际上我们只需要对每一对相邻的进行这种操作,就可以保证所有对都可以被线性表示出来,也自然就满足了所有的限制(这个地方自己推一下就知道了)。
考虑 可以无限大的情况,肯定是区间每一个数都相等,这时候根据算法算出来的是 ,刚好也和题目要求契合了。
时间复杂度允许的情况下可以直接 查询,但是如果只能 以下的话,只能用一些RMQ了。
但是这样就要注意,在合并两个区间的时候,不能直接把两份答案gcd起来,这样的话,并不能保证每一对都满足限制条件,比如各从两个区间内选一个,那么就不是满足的。因此我们在合并两个区间的时候还要注意 随便从两个区间里面分别取一个数gcd一下,然后丢到答案的GCD里面去。
这里ST表取的直接就是 ,线段树取的是 。
gcd不能写成 return x%y==0?y:gcd(y,x%y) 。不然会RE。
| #include<bits/stdc++.h> |
| using namespace std; |
| inline int gcd(int x,int y){return y==0?x:gcd(y,x%y);} |
| int T,n,q,l,r; |
| int a[200010],c[200010]; |
| int GCD[200010][22]; |
| inline int query(int l,int r) |
| { |
| if(l==r)return 0; |
| int loglen=(int)log2(r-l+1); |
| |
| |
| return gcd(abs(a[l]-a[r]),gcd(GCD[l][loglen],GCD[r-(1<<loglen)+1][loglen])); |
| } |
| inline void solve() |
| { |
| cin>>n>>q; |
| for(int i=1;i<=n;++i)cin>>a[i]; |
| for(int j=1;(1<<j)<=n;++j) |
| { |
| for(int i=1;i+(1<<j)-1<=n;++i) |
| { |
| if(j>1)GCD[i][j]=gcd(abs(a[i]-a[i+(1<<j-1)]),gcd(GCD[i][j-1],GCD[i+(1<<j-1)][j-1])); |
| else GCD[i][j]=abs(a[i]-a[i+(1<<j-1)]); |
| } |
| } |
| while(q--) |
| { |
| cin>>l>>r; |
| |
| cout<<query(l,r)<<'\n'; |
| } |
| cout<<'\n'; |
| } |
| int main() |
| { |
| ios::sync_with_stdio(0); |
| cin.tie(0),cout.tie(0); |
| cin>>T; |
| while(T--) |
| { |
| solve(); |
| } |
| return 0; |
| } |
| #include<bits/stdc++.h> |
| using namespace std; |
| const int N=2e5+10; |
| struct SEG |
| { |
| int l,r,val; |
| }tr[N<<2]; |
| int a[N]; |
| int T,n,q,l,r; |
| inline int gcd(int x,int y){return y==0?x:gcd(y,x%y);} |
| inline void pu(int x) |
| { |
| tr[x].val=gcd(abs(a[tr[x].l]-a[tr[x].r]),gcd(tr[x<<1].val,tr[x<<1|1].val)); |
| } |
| inline void build(int x,int l,int r) |
| { |
| tr[x].l=l,tr[x].r=r; |
| if(l==r){tr[x].val=0;return ;} |
| int mid=l+r>>1; |
| build(x<<1,l,mid),build(x<<1|1,mid+1,r); |
| pu(x); |
| } |
| inline int query(int x,int l,int r) |
| { |
| if(l<=tr[x].l&&tr[x].r<=r) |
| return tr[x].val; |
| int mid=tr[x].l+tr[x].r>>1; |
| if(l>mid)return query(x<<1|1,l,r); |
| else if(r<=mid)return query(x<<1,l,r); |
| else return gcd(abs(a[mid]-a[mid+1]),gcd(query(x<<1,l,r),query(x<<1|1,l,r))); |
| } |
| inline void solve() |
| { |
| cin>>n>>q; |
| for(int i=1;i<=n;++i)cin>>a[i]; |
| build(1,1,n); |
| while(q--) |
| { |
| cin>>l>>r; |
| if(l==r){cout<<0<<' ';continue;} |
| cout<<query(1,l,r)<<' '; |
| } |
| cout<<'\n'; |
| } |
| |
| int main() |
| { |
| ios::sync_with_stdio(0); |
| cin.tie(0),cout.tie(0); |
| cin>>T; |
| while(T--) |
| { |
| solve(); |
| } |
| return 0; |
| } |
这让我想到了之前有一场 div2 的 A,就是给定一个 ,然后求一个最小的 ,使得,这种情况可以证明最小的能够找到的 一定是。这个其实画一下数轴就可以直观地感受出来了。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!