「LibreOJ NOI Round #2」签到游戏
瞎猜一下我们只要\(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<bits/stdc++.h>
#define re register
#define LL long long
inline 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<b?a:b;}
int gcd(int a,int b) {return !b?a:gcd(b,a%b);}
struct Seg{int l,r,v;}b[2][105];
int n,Q,a[maxn];
int l[maxn<<2],r[maxn<<2],g[maxn<<2],pos[maxn];
inline void pushup(int i) {g[i]=gcd(g[i<<1],g[i<<1|1]);}
void build(int x,int y,int i) {
l[i]=x,r[i]=y;
if(x==y) {pos[x]=i;g[i]=a[x];return;}
int mid=x+y>>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<l[i]) return 0;
if(x<=l[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<l[i]) return 0;
if(x<=l[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<n) {
while(b[0][now].r<p) now++;
while(b[1][cnt].r<p) --cnt;
ans+=1ll*min(b[0][now].v,b[1][cnt].v)*(min(b[0][now].r,b[1][cnt].r)-p+1);
p=min(b[0][now].r,b[1][cnt].r)+1;
}
printf("%lld\n",ans+b[0][tot].v);
}
return 0;
}
「LibreOJ NOI Round #2」签到游戏的更多相关文章
- LOJ576 「LibreOJ NOI Round #2」签到游戏
题目 先进行一个转化: 每次花费\(\gcd\limits_{i=l+1}^rB_i\)的代价,可以连\((l,r)\)这一条边. 然后我们需要求\(0\sim n\)的最小生成树. 根据Kruska ...
- 「LibreOJ NOI Round #2」不等关系
「LibreOJ NOI Round #2」不等关系 解题思路 令 \(F(k)\) 为恰好有 \(k\) 个大于号不满足的答案,\(G(k)\) 表示钦点了 \(k\) 个大于号不满足,剩下随便填的 ...
- LibreOJ #507. 「LibreOJ NOI Round #1」接竹竿
二次联通门 : LibreOJ #507. 「LibreOJ NOI Round #1」接竹竿 /* LibreOJ #507. 「LibreOJ NOI Round #1」接竹竿 dp 记录一下前驱 ...
- 「LibreOJ NOI Round #1」验题
麻烦的动态DP写了2天 简化题意:给树,求比给定独立集字典序大k的独立集是哪一个 主要思路: k排名都是类似二分的按位确定过程. 字典序比较本质是LCP下一位,故枚举LCP,看多出来了多少个独立集,然 ...
- #509. 「LibreOJ NOI Round #1」动态几何问题
下面给出部分分做法和满分做法 有一些奇妙的方法可以拿到同样多的分数,本蒟蒻只能介绍几种常见的做法 如果您想拿18分左右,需要了解:质因数分解 如果您想拿30分左右,需要了解:一种较快的筛法 如果您想拿 ...
- #510. 「LibreOJ NOI Round #1」动态几何问题
题目: 题解: 几何部分,先证明一下 \(KX = \sqrt{a},YL = \sqrt{b}\) 设左侧的圆心为 \(O\) ,连接 \(OK\) ,我们有 \(OK = r\). 然后有 \(r ...
- #507. 「LibreOJ NOI Round #1」接竹竿 dp
题目: 题解: 我们考虑把每对花色相同的牌看作区间. 那么如果我们设 \(f_i\) 表示决策在 \([1,i]\) 内的最优答案. 那么有 \(f_i = max\{max\{(f_{j-1}+\s ...
- LOJ#510. 「LibreOJ NOI Round #1」北校门外的回忆(线段树)
题面 传送门 题解 感谢\(@M\_sea\)的代码我总算看懂题解了-- 这个操作的本质就是每次把\(x\)的\(k\)进制最低位乘\(2\)并进位,根据基本同余芝士如果\(k\)是奇数那么最低位永远 ...
- LOJ 510: 「LibreOJ NOI Round #1」北校门外的回忆
题目传送门:LOJ #510. 题意简述: 给出一个在 \(K\) 进制下的树状数组,但是它的实现有问题. 形式化地说,令 \(\mathrm{lowbit}(x)\) 为在 \(K\) 进制下的 \ ...
随机推荐
- error C3861: “L”: 找不到标识符
提示错误的语句:::CLSIDFromProgID(L("Shell.Application"), &clsid); 解决办法: 出现上面的错误是因为语法错误了,去掉字符串 ...
- 敏捷在《PMBOK指南》知识领域中的应用
<PMOBOK指南>知识领域 敏捷工作过程中的应用 第四章 项目整合管理 迭代和敏捷方法能够促进团队成员以相关领域专家的身份参与整合管理.团队成员自行决定计划及其组件的整合方式.在适应型环 ...
- python 一些特殊用法和坑
https://github.com/leisurelicht/wtfpython-cn
- 用 Flask 来写个轻博客 (1) — 创建项目
目录 目录 前言 扩展阅读 部署开发环境 创建 Github 项目 前言 一步一步的实现一个 Flask 轻博客项目启动,最新的代码会上传到 Github. 扩展阅读 欢迎使用 Flask - vir ...
- \t \r \n转义字符
t \r \n都是转义字符,空格就是单纯的空格,输入时可以输入空格 \t 的意思是 横向跳到下一制表符位置 \r 的意思是 回车 \n 的意思是回车换行 所有的转义字符和所对应的意义: 转义字符 意义 ...
- 在vue中获取微信支付code及code被占用问题的解决?
这个地方坑比较多,查看网上并没有详细的文档,新手一般写到这里很痛苦.这里我只介绍一下我解决的方案,虽然它不是最好的,但是可行的方案: 总体分两步 1)跳到微信支付链接,它会自动拼接上code 2)获取 ...
- Vue之数据排序加签
这篇随笔小编给大家带来的是数据排序加签: 所谓数据加签,就是把数据进行加密再传给后端,这样保证数据的秘密性.不容易被修改和获取:排序就是根据公司要求对字段进行排序,有些公司会把字段根据a-z排序,有些 ...
- style优先级
不同级别 在属性后面使用 !important 会覆盖页面内任何位置定义的元素样式. 作为style属性写在元素内的样式 id选择器 类选择器 标签选择器 通配符选择器 浏览器自定义或继承 ...
- 使用wireshark在windows平台下捕获HTTP协议数据包中的帐号密码信息
1.打开wireshark软件,从Interface List中选择相应的网卡,例如我的PC机上是“本地连接”,然后选择”Start”启动抓包程序. 2.打开学校主页,输入账号和密码登录校内邮箱. 3 ...
- C/C++ 智能指针
// 转载自 :https://www.cnblogs.com/wuyepeng/p/9741241.html { 为什么要使用智能指针:我们知道c++的内存管理是让很多人头疼的事,当我们写一个new ...