博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
「LibreOJ NOI Round #2」签到游戏
阅读量:5146 次
发布时间:2019-06-13

本文共 3086 字,大约阅读时间需要 10 分钟。

瞎猜一下我们只要\(n\)次询问就能确定出\(\{A_i\}\)

感受一下大概是询问的区间越长代价就越小,比如询问\([l,n]\)\([1,r]\)的代价肯定不会超过\([l,r]\)

所以大胆猜一下我们询问的只有一些前缀和后缀

首先我们肯定要询问一下\([1,n]\)的和,之后我们考虑顺次得到\(A_1\)\(A_n\)的和

想得到\(A_1\),我们当然可以直接询问\([1,1]\),但是有\([1,n]\)的和我们询问\([2,n]\)的和也能得到\(A_1\)

同理我们想得到\(A_i\),我们可以直接询问\([1,i]\)的和,由于这个时候\(A_1\)\(A_{i-1}\)的和都知道了,做一个差就能得到\(A_i\)的和;也可以询问\([i+1,n]\),也能得到\(A_i\)的和

于是我们维护一个前缀、后缀的\(\gcd\)显然哪个小选哪一个

这样复杂度是\(O(qn\log n)\)

考虑到一个序列的前后缀\(\gcd\)之会变化\(\log\)次,于是我们可以考虑用线段树来维护区间\(\gcd\),二分出每次变化的位置,一共要二分\(\log\)次,每次二分有一个\(\log\),查询前缀\(\gcd\)\(\log^2\),四个\(\log\)显然啥都过不去

首先求区间\(\gcd\)就是没有必要的,我们只需要对于二分出来的这段区间查询一下这段区间里是否所有数都是当前前缀\(\gcd\)的倍数就好了,只有全都都是当前前缀\(\gcd\)的倍数前缀\(\gcd\)才不会发生变化,于是线段树上的查询变成了\(\log n\)

再来考虑有线段树为什么还要生硬地套一个二分,我们直接在线段树上二分即可,对于一个当前的端点\(l\),我们直接在线段树上查\([l,n]\)这段区间,\([l,n]\)在线段树上被分成了\(\log\)段区间,我们依次访问到这些区间;当我们访问到一段区间的时候,发现如果这个区间的\(\gcd\)不是当前前缀\(\gcd\)的倍数,那么说明这个变化点肯定在这个区间内,我们直接在这颗子树里二分;否则我们再去访问接下来的区间,这样复杂度是两个\(\log\)相加

于是整体的复杂度就是\(O(q\log^2n)\)

代码

#include
#define re register#define LL long longinline int read() { char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;}const int maxn=1e5+5;inline int max(int a,int b) {return a>b?a:b;}inline int min(int a,int b) {return a
>1; build(x,mid,i<<1),build(mid+1,y,i<<1|1); pushup(i);}void change(int x,int v) { a[x]=v;x=pos[x];g[x]=v;x>>=1; while(x) pushup(x),x>>=1;}int chk(int i,int v) { if(l[i]==r[i]) return g[i]%v==0?l[i]:l[i]-1; if(g[i<<1]%v==0) return chk(i<<1|1,v); return chk(i<<1,v);}int find(int x,int y,int i,int v) { if(x>r[i]||y
=r[i]) { if(g[i]%v==0) return r[i]; return chk(i,v); } int mid=l[i]+r[i]>>1; int now=find(x,y,i<<1,v); if(now!=mid&&now) return now; return max(now,find(x,y,i<<1|1,v));}int calc(int i,int v) { if(l[i]==r[i]) return g[i]%v==0?l[i]:l[i]+1; if(g[i<<1|1]%v==0) return calc(i<<1,v); return calc(i<<1|1,v);}int query(int x,int y,int i,int v) { if(x>r[i]||y
=r[i]) { if(g[i]%v==0) return l[i]; return calc(i,v); } int mid=l[i]+r[i]>>1; int now=query(x,y,i<<1|1,v); if(now&&now!=mid+1) return now; int t=query(x,y,i<<1,v); if(t) return t;return now;}int main() { n=read(),Q=read(); for(re int i=1;i<=n;i++) a[i]=read(); build(1,n,1);int x,v,tot,cnt,now,p; while(Q--) { x=read(),v=read();change(x,v); tot=0;b[0][++tot].l=1; while(b[0][tot].l<=n) { b[0][tot].v=gcd(b[0][tot-1].v,a[b[0][tot].l]); b[0][tot].r=find(b[0][tot].l,n,1,b[0][tot].v);++tot; b[0][tot].l=b[0][tot-1].r+1; } --tot; cnt=0;b[1][++cnt].r=n; while(b[1][cnt].r>=1) { b[1][cnt].v=gcd(b[1][cnt-1].v,a[b[1][cnt].r]); b[1][cnt].l=query(1,b[1][cnt].r,1,b[1][cnt].v);++cnt; b[1][cnt].r=b[1][cnt-1].l-1; } --cnt;now=1;p=1; for(re int i=1;i<=cnt;i++) b[1][i].l--,b[1][i].r--; LL ans=0; while(p

转载于:https://www.cnblogs.com/asuldb/p/11543148.html

你可能感兴趣的文章
Codeforces Round #277 (Div. 2)
查看>>
一步步学Mybatis-搭建最简单的开发环境-开篇(1)
查看>>
微信小程序图片上传
查看>>
【更新】智能手机批量添加联系人
查看>>
NYOJ-128前缀式计算
查看>>
centos6.7 配置外网端口映射
查看>>
红外通信基础(含代码)
查看>>
淡定,啊。数据唯一性
查看>>
java并发编程之lock锁
查看>>
深入理解 JavaScript 事件循环(一)— event loop
查看>>
Hive(7)-基本查询语句
查看>>
常用第三方(分享,支付,二维码,语音,推送)
查看>>
Redis快速入门
查看>>
动态绑定时的显示隐藏控制
查看>>
注意java的对象引用
查看>>
C++ 面向对象 类成员函数this指针
查看>>
inline函数的总结
查看>>
SPSS-生存分析
查看>>
【Jquery】$.Deferred 对象
查看>>
linux IPC
查看>>