20250611 NOI 模拟赛

20250611 NOI 模拟赛

Problem A. 挑战

Description

现有两个长为 \(n\) 的序列 \(A,B\)\(A\) 未知,\(B\) 已知。

每次可以花费 \(\gcd_{i=l}^r b_i\) 的代价知道 \(\sum_{i=l}^r a_i\) 的值。目标是求出每个 \(A_i\) 的值,求最小代价。

还有 \(Q\) 次对 \(B\) 的单点修改,每次修改后输出答案。

\(1\leq n,Q\leq 10^5\)

Solution

考虑对 \(A\) 做前缀和得到 \(S\)。每次查询相当于知道了 \(S_r-S_{l-1}\) 的值。

考虑做如下转化:查询 \((l,r)\),就在 \(l-1,r\) 之间连一条边。当 \(0\sim n\) 都连通时,便求出了答案。

所以问题转化为了一个 MST 问题。

考虑 Kruskal 的想法。\((0,n)\) 最小,一定要选;接下来是 \((0,n-1),(1,n)\),以此类推。

然后可以发现,每次加入的边一定有一个端点是 \(0\)\(n\),否则一定不优。于是做到 \(O(Qn\log n)\) 的复杂度。

\(w(l,r)=\gcd_{i=l}^r b_i\)\(w(1,i)\) 不升,\(w(i+1,n)\) 不降,所以存在一个 \(p\)\([1,p]\) 全部向 \(0\) 连边,\([p+1,n-1]\) 全部向 \(n\) 连边。这个 \(p\) 可以二分得到。

答案即为 \(w(1,n)+\sum_{i=1}^p w(i+1,n)+\sum_{i=p+1}^{n-1}w(1,i)=-w(1,n)+\sum_{i=1}^{p+1}w(i,n)+\sum_{i=p+1^n}w(1,i)\)

由于不同的 \(w(1,i),w(i,n)\) 最多不超过 \(O(\log V)\) 种,所以容易维护出答案。

\(\log n,\log V\) 同阶,时间复杂度 \(O(n\log^3 n)\)

int n,Q,a[N];

int tr[N<<2];

void Buildtr(int p,int l,int r){
	if(l==r) return tr[p]=a[l],void();
	int mid=(l+r)>>1;
	Buildtr(p<<1,l,mid);
	Buildtr(p<<1|1,mid+1,r);
	tr[p]=__gcd(tr[p<<1],tr[p<<1|1]);
}

void Update(int p,int l,int r,int x){
	if(l==r) return tr[p]=a[l],void();
	int mid=(l+r)>>1;
	if(x<=mid) Update(p<<1,l,mid,x);
	else Update(p<<1|1,mid+1,r,x);
	tr[p]=__gcd(tr[p<<1],tr[p<<1|1]);
}

int Find1(int p,int l,int r,int k,int d){
	if(l==r) return l;
	int mid=(l+r)>>1,val=__gcd(d,tr[p<<1]);
	if(val<k) return Find1(p<<1,l,mid,k,d);
	else return Find1(p<<1|1,mid+1,r,k,val);
}

int Find2(int p,int l,int r,int k,int d){
	if(l==r) return l;
	int mid=(l+r)>>1,val=__gcd(d,tr[p<<1|1]);
	if(val<k) return Find2(p<<1|1,mid+1,r,k,d);
	else return Find2(p<<1,l,mid,k,val);
}

int Ask(int p,int l,int r,int L,int R){
	if(L<=l&&r<=R) return tr[p];
	int mid=(l+r)>>1;
	if(L<=mid&&mid<R)
		return __gcd(Ask(p<<1,l,mid,L,R),Ask(p<<1|1,mid+1,r,L,R));
	else if(L<=mid) return Ask(p<<1,l,mid,L,R);
	else return Ask(p<<1|1,mid+1,r,L,R);
}

struct Node{
	int x,v;
};

signed main(){
	FileIO();
	read(n),read(Q);
	for(int i=1;i<=n;i++) read(a[i]);
	Buildtr(1,1,n);
	while(Q--){
		int x,v; read(x),read(v);
		a[x]=v; Update(1,1,n,x); 
		vector<Node> s,t;
		s.reserve(32),t.reserve(32);
		s.push_back(Node{1,a[1]});
		while(1){
			int p=Find1(1,1,n,s.back().v,0);
			s.push_back({p,Ask(1,1,n,1,p)});
			if(s.back().v==tr[1]) break;
		}
		s.push_back(Node{n+1,0});
		t.push_back(Node{n,a[n]});
		while(1){
			int p=Find2(1,1,n,t.back().v,0);
			t.push_back({p,Ask(1,1,n,p,n)});
			if(t.back().v==tr[1]) break;
		}
		t.push_back(Node{0,0});
		int l=1,r=n-1,pos=0;
		while(l<=r){
			int mid=(l+r)>>1;
			if(Ask(1,1,n,mid+1,n)<=Ask(1,1,n,1,mid)) pos=mid,l=mid+1;
			else r=mid-1;
		}
		ll ans=0;
		for(int i=(signed)t.size()-2;i>=0;i--){
			int l=t[i+1].x+1,r=min(pos+1,t[i].x);
			ans+=1ll*(r-l+1)*t[i].v;
			if(r==pos+1) break;
		}
		for(int i=(signed)s.size()-2;i>=0;i--){
			int l=max(pos+1,s[i].x),r=s[i+1].x-1;
			ans+=1ll*(r-l+1)*s[i].v;
			if(l==pos+1) break;
		}
		printf("%lld\n",ans-tr[1]);
	}
    return 0;
}

posted @ 2025-06-11 19:12  XP3301_Pipi  阅读(11)  评论(0)    收藏  举报
Title