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;
}

浙公网安备 33010602011771号