LOJ576签到游戏
题目的询问是个经典模型。
令\(s_i=a_1+a_2+...+a_i\)(前缀和),则我们询问一下能够得到\(s_r-s_{l-1}\)
考虑图\(0...n\)的一个生成树,边\((l,r)\)的代价是\(\gcd(a_{l+1}...a_r)\)
显然我们得知了生成树,即可推出所有\(s\),所以我们要求最小生成树。
但是直接mst会超时。
考虑画图,画图后会发现一个点要么连向\(0\),要么连向\(n\)。
由于一个点不能连出自环,所以显然\(0\)要连向\(n\)。
其他点显然我们会选择边权最小的点连接。
考虑把所有后缀,前缀\(\gcd\)相同连续段抽出来。只有\(\log_2\)种。
则考虑扫描线。显然可以轻松的计算出代价。
现在考虑怎么得知\(\gcd\)相同连续段。
显然可以每次线段树二分。
好像直接暴力\(\gcd\)时间复杂度就是对的。
然而T了。改成取模就过了
#include<bits/stdc++.h>
using namespace std;
#define N 400010
#define int long long
int gcd(int a,int b){
if(!b)
return a;
return gcd(b,a%b);
}
int n,q,b[N],g[N],p[N],s[N],e[N],f[N],ct,rr[N],h[N],cc,v1[N],v2[N];
int fd(int x){
return p[x]==x?x:p[x]=fd(p[x]);
}
struct no{
int x,y,z;
}a[N];
struct nn{
int l,r,v;
}c[N],d[N];
int operator <(no x,no y){
return x.z<y.z;
}
void up(int o){
g[o]=gcd(g[o*2],g[o*2+1]);
}
void bd(int o,int l,int r){
if(l==r){
g[o]=b[l];
return;
}
int md=(l+r)/2;
bd(o*2,l,md);
bd(o*2+1,md+1,r);
up(o);
}
void mod(int o,int l,int r,int x,int y){
if(l==r){
g[o]=y;
return;
}
int md=(l+r)/2;
if(x<=md)
mod(o*2,l,md,x,y);
else
mod(o*2+1,md+1,r,x,y);
up(o);
}
int gt(){
int ct=0,v=0;
for(int i=0;i<=n;i++)
p[i]=i;
for(int i=1;i<=n;i++)
v=gcd(v,b[i]);
a[++ct]=(no){0,n,v};
int ans=0;
v=0;
for(int i=1;i<n;i++){
v=gcd(v,b[i]);
a[++ct]=(no){0,i,v};
}
v=0;
for(int i=n;i>1;i--){
v=gcd(v,b[i]);
a[++ct]=(no){i-1,n,v};
}
sort(a+1,a+ct+1);
for(int i=1;i<=ct;i++){
int xx=fd(a[i].x),yy=fd(a[i].y);
if(xx!=yy){
p[xx]=yy;
ans+=a[i].z;
}
}
return ans;
}
void fd(int o,int l,int r,int x,int y){
if(r<x||y<l)
return;
if(x<=l&&r<=y){
s[++ct]=g[o];
e[ct]=l;
f[ct]=r;
rr[ct]=o;
return;
}
int md=(l+r)/2;
fd(o*2,l,md,x,y);
fd(o*2+1,md+1,r,x,y);
}
int e1(int o,int l,int r,int c){
if(l==r)
return l;
int md=(l+r)/2;
if(g[o*2]%c!=0)
return e1(o*2,l,md,c);
else
return e1(o*2+1,md+1,r,c);
}
int e2(int o,int l,int r,int c){
if(l==r)
return l;
int md=(l+r)/2;
if(g[o*2+1]%c!=0)
return e2(o*2+1,md+1,r,c);
else
return e2(o*2,l,md,c);
}
int f1(int x,int g,int lp){
ct=0;
fd(1,1,n,x,n);
int v=0;
for(int i=1;i<=ct;i++)
v=gcd(v,s[i]);
v=gcd(v,lp);
if(v%g==0)
return -1;
for(int i=1;i<=ct;i++){
int p=gcd(lp,s[i]);
if(p!=g)
return e1(rr[i],e[i],f[i],g);
lp=p;
}
}
int f2(int x,int g,int lp){
ct=0;
fd(1,1,n,1,x);
int v=0;
for(int i=1;i<=ct;i++)
v=gcd(v,s[i]);
v=gcd(v,lp);
if(v%g==0)
return -1;
for(int i=ct;i;i--){
int p=gcd(lp,s[i]);
if(p!=g)
return e2(rr[i],e[i],f[i],g);
lp=p;
}
}
signed main(){
scanf("%lld%lld",&n,&q);
for(int i=1;i<=n;i++)
scanf("%lld",&b[i]);
if(n*q<=100000){
for(int i=1;i<=q;i++){
int x,y;
scanf("%lld%lld",&x,&y);
b[x]=y;
printf("%lld\n",gt());
}
return 0;
}
bd(1,1,n);
while(q--){
int x,y;
scanf("%lld%lld",&x,&y);
mod(1,1,n,x,y);
b[x]=y;
cc=0;
int p=1,la=0,c1=0,c2=0,gg=b[1],c3=0,c4=0,lp=0;
while(p!=-1){
la=p;
p=f1(p,gg,lp);
if(p==-1){
if(la<=n)
c[++c1]=(nn){la,n,gg};
break;
}
if(la<n)
c[++c1]=(nn){la,p-1,gg};
gg=gcd(gg,b[p]);
lp=gcd(lp,b[la]);
}
p=n;
la=n;
gg=b[n];
lp=0;
while(p!=-1){
la=p;
p=f2(p,gg,lp);
if(p==-1){
if(la)
d[++c2]=(nn){1,la,gg};
break;
}
if(la)
d[++c2]=(nn){p+1,la,gg};
gg=gcd(gg,b[p]);
lp=gcd(lp,b[la]);
}
for(int i=1;i<=c2;i++){
d[i].l--;
d[i].r--;
d[i].l=max(d[i].l,1ll);
}
for(int i=1;i<=c1;i++){
h[++cc]=c[i].l;
h[++cc]=c[i].r+1;
}
for(int i=1;i<=c2;i++){
h[++cc]=d[i].l;
h[++cc]=d[i].r+1;
}
int ans=0;
sort(h+1,h+cc+1);
h[cc+1]=n;
cc=unique(h+1,h+cc+1)-h-1;
for(int i=1;i<=c1;i++){
c[i].l=lower_bound(h+1,h+cc+1,c[i].l)-h;
c[i].r=upper_bound(h+1,h+cc+1,c[i].r)-h-1;
}
for(int i=1;i<=c2;i++){
d[i].l=lower_bound(h+1,h+cc+1,d[i].l)-h;
d[i].r=upper_bound(h+1,h+cc+1,d[i].r)-h-1;
}
for(int i=1;i<=c1;i++)
for(int j=c[i].l;j<=c[i].r;j++)
v1[j]=c[i].v;
for(int i=1;i<=c2;i++)
for(int j=d[i].l;j<=d[i].r;j++)
v2[j]=d[i].v;
for(int i=1;i<=cc;i++)
ans+=max(0ll,min(h[i+1],n)-h[i])*min(v1[i],v2[i]);
for(int i=1;i<=cc;i++)
v1[i]=v2[i]=h[i]=0;
printf("%lld\n",ans+g[1]);
}
puts("");
}