题目

先进行一个转化:

每次花费\(\gcd\limits_{i=l+1}^rB_i\)的代价,可以连\((l,r)\)这一条边。

然后我们需要求\(0\sim n\)的最小生成树。

根据Kruskal的思想,\((0,n)\)这条边一定会被选。

然后根据Prim的思想,对于某个点,我们需要找到其最短的出边。

而显然对于\(i\),它最短的出边为\((i,0)\)或者\((i,n)\)。边权为\(L_i=\gcd\limits_{j=1}^iB_j\)和\(R_i=\gcd\limits_{j=i+1}^nB_j\)。

显然\(L_i\)是单调不增,\(R_i\)是单调不减的。

所以\(\exists p\in[0,n),\forall i\in[0,p],R_i\le L_i,\forall i\in(p,n),L_i\le R_i\)。

我们可以用线段树维护每个区间\([l,r]\)的\(\gcd\limits_{i=l+1}^rB_i\),然后在线段树上二分求出\(p\)。

而题目所给的修改可以直接单点修改。

剩下的就是求\(\sum\limits_{i=0}^pR_i+\sum\limits_{i=p+1}^{n-1}L_i\)。

考虑到\(L_i\)以及\(R_i\)的取值个数是\(\log n\)级别的,我们可以在线段树上暴力找出这些取值以及其对应的区间。

#include<cstdio>
#include<cctype>
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1)
#define ll long long
namespace IO
{
char ibuf[(1<<21)+1],obuf[(1<<21)+1],stk[19],*iS,*iT,*oS=obuf,*oT=obuf+(1<<21);
char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
void Flush(){fwrite(obuf,1,oS-obuf,stdout),oS=obuf;}
void Put(char x){*oS++=x;if(oS==oT)Flush();}
int read(){int x=0,c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return x;}
void write(ll x){int top=0;while(x)stk[++top]=(x%10)+48,x/=10;while(top)Put(stk[top--]);Put('\n');}
}
using namespace IO;
int gcd(int n,int m){return !m||!n? n+m:gcd(m,n%m);}
int t[400007];
void build(int p,int l,int r)
{
if(l==r) return (void)(t[p]=read());
build(ls,l,mid),build(rs,mid+1,r),t[p]=gcd(t[ls],t[rs]);
}
void update(int p,int l,int r,int x,int v)
{
if(l==r) return (void)(t[p]=v);
x<=mid? update(ls,l,mid,x,v):update(rs,mid+1,r,x,v);t[p]=gcd(t[ls],t[rs]);
}
int Find(int p,int l,int r,int a,int b)
{
if(l==r) return l;
int x=gcd(a,t[ls]),y=gcd(b,t[rs]);
return x<=y? Find(ls,l,mid,a,y):Find(rs,mid+1,r,x,b);
}
ll cal1(int p,int l,int r,int x,int v)
{
if(l==r) return gcd(t[p],v);
int a=gcd(t[rs],v),b=gcd(t[ls],a);
return x<=mid? cal1(ls,l,mid,x,a):(a==b? 1ll*(mid-l+1)*a:cal1(ls,l,mid,x,a))+cal1(rs,mid+1,r,x,v);
}
ll cal2(int p,int l,int r,int x,int v)
{
if(l==r) return gcd(t[p],v);
int a=gcd(t[ls],v),b=gcd(t[rs],a);
return x>mid? cal2(rs,mid+1,r,x,a):(a==b? 1ll*(r-mid)*a:cal2(rs,mid+1,r,x,a))+cal2(ls,l,mid,x,v);
}
int main()
{
int n=read(),Q=read();
build(1,1,n);
for(int p,v;Q;--Q) p=read(),v=read(),update(1,1,n,p,v),p=Find(1,1,n,0,0),write(cal1(1,1,n,p,0)+cal2(1,1,n,p,0)-t[1]);
return Flush(),0;
}

最新文章

  1. salesforce 零基础学习(五十二)Trigger使用篇(二)
  2. SQL Server Replication 中关于视图的点滴
  3. Other linker flags
  4. android开发时,finish()跟System.exit(0)的区别
  5. lc面试准备:Implement Stack using Queues
  6. Java中的一些常见错误
  7. lvs keepalived 安装配置详解【转】
  8. JavaScript基础知识----document对象
  9. 【七】注入框架RoboGuice使用:(Your First Custom Binding)
  10. plsql部分字段中文乱码,sqlplus中文乱码
  11. 机器学习之Logistic 回归算法
  12. js备战春招の四のdevtool中各种错误、调试的使用技巧
  13. Java Math的 floor,round和ceil
  14. SQL—访问操作(2)
  15. JavaScript中call和apply方法的使用
  16. 【Java每日一题】20170329
  17. 如何调用wasm文件?
  18. CentOS7 限制SSH密码尝试次数
  19. jumpserver堡垒机web终端支持复制粘贴功能
  20. linq时间筛选以及list时间筛选

热门文章

  1. webpack官方文档分析(一):安装
  2. rabbitmq 的安装配置使用
  3. Vue_(组件通讯)单项数据流
  4. RTS打卡计划第四周
  5. Git常用命令详解
  6. 用 dnSpy 反编译调试 .NET 程序
  7. ExpectedConditions API
  8. DP----鬼畜的数字三角形
  9. druid连接池各属性说明
  10. 深入理解红黑树及C++实现