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("");
}
posted @ 2020-12-18 07:49  celerity1  阅读(89)  评论(0编辑  收藏  举报