Loj#576-「LibreOJ NOI Round #2」签到游戏【线段树】

正题

题目链接:https://loj.ac/p/576


题目大意

给出一个长度为\(n\)的序列\(a\),还有一个未知序列\(b\),你每次可以花费\(\gcd_{i=l}^r a_i\)的代价得到\(\sum_{i=l}^rb_i\)的值。

每次修改\(a\)中的一个数,求得到\(b\)中所有数字需要花费的最小权值。

\(1\leq n,q\leq 10^5,1\leq a_i\leq 10^9\)


解题思路

因为有一个信息上的问题,有限的信息对应的结果肯定不能超过信息的数量。所以我们需要知道\(n\)个数字那么我们就至少需要询问\(n\)次。

然后考虑\(s_i=\sum_{j=1}^i b_j\),那么我们每次得到的将是一个\(s_r-s_{l-1}\),我们可以通过一个\([l,r]\)\([l',r]\)的询问得到\([l,l'-1]\)(反过来同理)。也就是一些端点相互连接相互覆盖的区间我们可以把它们转换为端点相同但是没有相互覆盖的区间。

然后我们目标是所有端点都得在里面,加上相互连接这个限制,看上去就很像最小生成树。相当于有点\([0,n]\),我们连接\(l,r\)的代价是\(\gcd_{i=l+1}^ra_i\),求最小生成树。

然后显然的我们肯定是找一个\(k\),然后\(0\sim k\)\(n\)连边,\(k+1\sim n\)\(0\)连边,主要是找到这个\(k\),这个\(k\)就是第一个满足\(\gcd_{i=1}^k a_i\leq \gcd_{i=k}^n a_i\)的位置。

然后因为前后缀\(gcd\)都最多变化\(\log\)次,我们可以考虑求出这些位置。

考虑在线段树上二分出这些位置,看上去是\(\log^3\)的,实际上假设\(L\sim mid\)中没有答案,那么此时这个前缀的\(gcd\)依旧等于\(val\),所以不需要管前面的东西,到一个位置的时候判这个区间的值是不是\(val\)的倍数就好了。

时间复杂度:\(O(n\log^2 n)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define mp(x,y) make_pair(x,y)
#define ll long long
using namespace std;
const ll N=1e5+10;
ll n,q,a[N],w[N<<2];
vector<pair<ll,ll> > pre,suf;
void Change(ll x,ll L,ll R,ll pos,ll val){
	if(L==R){w[x]=val;return;}
	ll mid=(L+R)>>1;
	if(pos<=mid)Change(x*2,L,mid,pos,val);
	else Change(x*2+1,mid+1,R,pos,val);
	w[x]=__gcd(w[x*2],w[x*2+1]);return;
}
ll AskP(ll x,ll L,ll R,ll pos,ll &val){
	if(w[x]%val==0)return n;
	if(L==R){val=__gcd(val,w[x]);return L;}
	ll mid=(L+R)>>1;
	if(pos>mid)return AskP(x*2+1,mid+1,R,pos,val);
	ll k=AskP(x*2,L,mid,pos,val);
	if(k==n)return AskP(x*2+1,mid+1,R,pos,val);
	return k;
}
ll AskS(ll x,ll L,ll R,ll pos,ll &val){
	if(w[x]%val==0)return 0;
	if(L==R){val=__gcd(val,w[x]);return L;}
	ll mid=(L+R)>>1;
	if(pos<=mid)return AskS(x*2,L,mid,pos,val);
	ll k=AskS(x*2+1,mid+1,R,pos,val);
	if(!k)return AskS(x*2,L,mid,pos,val);
	return k;
}
ll Ask(ll x,ll L,ll R,ll l,ll r){
	if(L==l&&R==r)return w[x];
	int mid=(L+R)>>1;
	if(r<=mid)return Ask(x*2,L,mid,l,r);
	if(l>mid)return Ask(x*2+1,mid+1,R,l,r);
	return __gcd(Ask(x*2,L,mid,l,mid),Ask(x*2+1,mid+1,R,mid+1,r));
} 
signed main()
{
//	freopen("game3_12.in","r",stdin);
	scanf("%lld%lld",&n,&q);ll d=0;n++;
	for(ll i=1;i<n;i++)
		scanf("%lld",&a[i]),Change(1,0,n,i,a[i]);
	Change(1,0,n,0,1);Change(1,0,n,n,1);
	while(q--){
		ll p,x;pre.clear();suf.clear();
		scanf("%lld%lld",&p,&x);
		Change(1,0,n,p,x);a[p]=x;
		ll val=a[1];x=1;
		while(x<n){
			pre.push_back(mp(x,val));
			x=AskP(1,0,n,x+1,val);
		}
		x=n-1;val=a[n-1];
		while(x){
			suf.push_back(mp(x-1,val));
			x=AskS(1,0,n,x-1,val);
		}
		ll p1=pre.size()-1,p2=suf.size()-1;
		ll L=-1,R=n,ans=0;
		while(1){
			if(p2<0||pre[p1].second<suf[p2].second){
				if(pre[p1].first<=L){
					ans+=pre[p1].second*(R-L-1);
					break;
				}
				ans+=pre[p1].second*(R-pre[p1].first);
				R=pre[p1].first;p1--;
			}
			else{
				if(suf[p2].first>=R){
					ans+=suf[p2].second*(R-L-1);
					break;
				}
				ans+=suf[p2].second*(suf[p2].first-L);
				L=suf[p2].first;p2--;
			}
		}
		printf("%lld\n",ans-Ask(1,0,n,1,n-1));
	}
	return 0;
}
//2 6 3
posted @ 2022-07-14 11:24  QuantAsk  阅读(48)  评论(0编辑  收藏  举报