正题
题目链接:https://loj.ac/p/576
题目大意
给出一个长度为nnn的序列aaa,还有一个未知序列bbb,你每次可以花费gcdi=lrai\gcd_{i=l}^r a_igcdi=lrai的代价得到∑i=lrbi\sum_{i=l}^rb_i∑i=lrbi的值。
每次修改aaa中的一个数,求得到bbb中所有数字需要花费的最小权值。
1≤n,q≤105,1≤ai≤1091\leq n,q\leq 10^5,1\leq a_i\leq 10^91≤n,q≤105,1≤ai≤109
解题思路
因为有一个信息上的问题,有限的信息对应的结果肯定不能超过信息的数量。所以我们需要知道nnn个数字那么我们就至少需要询问nnn次。
然后考虑si=∑j=1ibjs_i=\sum_{j=1}^i b_jsi=∑j=1ibj,那么我们每次得到的将是一个sr−sl−1s_r-s_{l-1}sr−sl−1,我们可以通过一个[l,r][l,r][l,r]和[l′,r][l',r][l′,r]的询问得到[l,l′−1][l,l'-1][l,l′−1](反过来同理)。也就是一些端点相互连接相互覆盖的区间我们可以把它们转换为端点相同但是没有相互覆盖的区间。
然后我们目标是所有端点都得在里面,加上相互连接这个限制,看上去就很像最小生成树。相当于有点[0,n][0,n][0,n],我们连接l,rl,rl,r的代价是gcdi=l+1rai\gcd_{i=l+1}^ra_igcdi=l+1rai,求最小生成树。
然后显然的我们肯定是找一个kkk,然后0∼k0\sim k0∼k向nnn连边,k+1∼nk+1\sim nk+1∼n向000连边,主要是找到这个kkk,这个kkk就是第一个满足gcdi=1kai≤gcdi=knai\gcd_{i=1}^k a_i\leq \gcd_{i=k}^n a_igcdi=1kai≤gcdi=knai的位置。
然后因为前后缀gcdgcdgcd都最多变化log\loglog次,我们可以考虑求出这些位置。
考虑在线段树上二分出这些位置,看上去是log3\log^3log3的,实际上假设L∼midL\sim midL∼mid中没有答案,那么此时这个前缀的gcdgcdgcd依旧等于valvalval,所以不需要管前面的东西,到一个位置的时候判这个区间的值是不是valvalval的倍数就好了。
时间复杂度:O(nlog2n)O(n\log^2 n)O(nlog2n)
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