思路:
主要考虑子区间gcd等于区间gcd的数量怎么求。gcd有一个性质:越多的数的gcd一定是单调不上升的。也就是说父亲区间的gcd一定是整个父亲区间里任何子区间的最小gcd,包括左右儿子区间的gcd。这题记录子区间gcd与区间gcd相等的数量和记录不相等的数量是一样的。
以记录不相等的数量为例。
对于一个区间长度为len的区间,它的所有连续子区间方法数是(len+1)*len/2,也就是子区间长度为1的数量一直加到子区间长度为len的数量。我们考虑区间不相等gcd子区间数量怎么合并。分为三部分:
1.只包含左区间
2.只包含右区间
3.左右区间都包含
前两种是一样的。我们记父亲区间是u,左儿子区间是lu,右儿子区间是ru。如果u.gcd==lu.gcd,那么lu的贡献就是lu.nog_cnt,因为lu.nog_cnt就是只包括左区间的gcd!=u.gcd的数量,同理右区间贡献就是ru.nog_cnt。如果u.gcd!=lu.gcd,那么lu的贡献就是(l.len+1)*l.len/2,因为lu.gcd肯定是大于u.gcd的,gcd具有单调性。当lu.gcd都大于u.gcd的时候,也就是说无论lu的子区间是什么gcd都是大于u.gcd,所以子区间的贡献就是(l.len+1)*l.len/2。右区间同理。
接下来我们考虑跨左右区间的贡献。假如我们固定了左区间的位置idxl,我们考虑右区间有贡献的位置idxr。由于gcd具有单调性,区间范围越大gcd一定不上升,也就是说只有当idxr变小时,区间(idxl,idxr)的gcd才有可能变大。同时u.gcd又是区间最小gcd,所以只要idxr减小到区间(idxl,idxr)的gcd大于u.gcd时,(0,idxr)区间的idxr都是合法的,也就是这时候对于某个固定的idxl它的贡献就是(0,idxr)的大小。
接下来我们考虑idxl的移动。同样的因为gcd的单调性,如果idxl减小的时候,区间(idxl,idxr)的gcd一定是单调不上升的,也就是说当idxl减小时,区间(idxl,idxr)的gcd又可能恢复到u.gcd,这时候我们只需要继续让idxr减小即可。
容易发现对于idxr来说,随着idxl的减小,idxr也一定是减小的,所以我们就可以用双指针的方式,用o(n)的时间来遍历每个idxl的值,求出每个idxl的贡献,最后它们的贡献和就是跨区间的贡献。在遍历idxl和idxr的时候,我们可以根据gcd的性质来稍微优化一下,如果区间(l,r)的gcd是一样的,我们就可以将它们当成一个值,因为这整个区间的gcd是一样。我们用pair<gcd,cnt>来表示某个连续的gcd的值有多少个。
在合并区间的时候,我们需要用到左区间从右端点到idxl的区间gcd,我们即为ne,和右区间的左端点到idxr的区间gcd,我们即为pre,我们可以在合并的时候一起将pre和ne更新。
接下来我们考虑pre和ne怎么更新。仍然记父亲区间为u,左儿子为lu,右儿子为ru。pre表示的是区间的左端到idxl的区间gcd,所以对于u.pre的左半区间我们可以直接将lu.pre赋值给u.pre,这部分区间是已经求过的了。对于剩下的ru区间我们从到右遍历一次,我们仍然是将gcd一样的值用一个pair<gcd,cnt>来记录,也就是说如果gcd(l,idxl+1)==gcd(l,idxl),我们就让pair<gcd(l,idxl),cnt>的cnt++。如果gcd(l,idxl+1)!=gcd(l,idxl),我们就新开一个pair记为<gcd(l,idxl+1),1>。ne同理。
nog_cnt的代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const int inf=0x3f3f3f3f;
typedef long long ll;
typedef pair<int,int> pii;
typedef unsigned long long ull;
//#define int long long
//const ll P=2281701377;
const ll P=998244353;
const int mod=1e9+7;
#define fi first
#define se second
ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
int n,m;
int a[N];
struct node{
int l,r;
int len;
int g;
ll nog_cnt;
vector<pii> pre,ne;
}tr[N*4];
void pushup(node &u,node &l,node &r){
u.len=l.len+r.len;
u.g=gcd(l.g,r.g);
u.nog_cnt=0;
u.nog_cnt+=(l.g==u.g?l.nog_cnt:(ll)(l.len+1)*l.len/2);
u.nog_cnt+=(r.g==u.g?r.nog_cnt:(ll)(r.len+1)*r.len/2);
ll sum=0;
for(int i=0;i<r.pre.size();i++)
sum+=r.pre[i].se;
int idx=r.pre.size()-1;
ll ans=0;
for(int i=0;i<l.ne.size();i++){
while(idx>=0&&gcd(l.ne[i].fi,r.pre[idx].fi)==u.g){
sum-=r.pre[idx].se;
idx--;
}
ans+=(ll)sum*l.ne[i].se;
}
u.nog_cnt+=ans;
u.pre=l.pre;
u.ne=r.ne;
for(int i=0;i<r.pre.size();i++){
if(r.pre[i].fi%u.pre.back().fi==0)
u.pre.back().se+=r.pre[i].se;
else
u.pre.push_back({gcd(u.pre.back().fi,r.pre[i].fi),r.pre[i].se});
}
for(int i=0;i<l.ne.size();i++){
if(l.ne[i].fi%u.ne.back().fi==0)
u.ne.back().se+=l.ne[i].se;
else
u.ne.push_back({gcd(u.ne.back().fi,l.ne[i].fi),l.ne[i].se});
}
}
void build(int u,int l,int r){
tr[u]={l,r};
tr[u].nog_cnt=0;
tr[u].pre.clear();
tr[u].ne.clear();
if(l==r){
tr[u]={l,r,1,a[l],0};
tr[u].pre.push_back({a[l],1});
tr[u].ne.push_back({a[l],1});
return;
}
int mid=(l+r)>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}
node query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r){
return tr[u];
}
int mid=tr[u].l+tr[u].r>>1;
if(r<=mid) return query(u<<1,l,r);
else if(l>mid) return query(u<<1|1,l,r);
else{
node no1=query(u<<1,l,r);
node no2=query(u<<1|1,l,r);
node ans;
pushup(ans,no1,no2);
return ans;
}
}
int cas=0;
void solve(){
cout<<"Case #"<<++cas<<":"<<endl;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
build(1,1,n);
cin>>m;
while(m--){
int l,r;
cin>>l>>r;
node no=query(1,l,r);
ll ans1=no.g;
ll ans2=(ll)(no.len+1)*no.len/2-no.nog_cnt;
cout<<ans1<<' '<<ans2<<endl;
}
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
cin>>t;
while(t--){
solve();
}
}
要是改成记录相等数量的时候,我们记父亲区间为u,左儿子为lu,右儿子为ru。如果lu.gcd==u.gcd,贡献仍然是lu.g_cnt,如果lu.gcd!=u.gcd,贡献就是0,因为无论lu子区间怎么取,子区间的gcd都是大于u.gcd的。右区间同理。
跨区间的时候,同样是利用gcd的单调性。因为我们是求gcd相等的情况,也就是说区间越大gcd会越小,所以我们这次让idxl从小到大,idxr从小到大。这样当我们先从固定idxl取到一个最大idxr时,idxr的右边都是符合条件的。当idxl变大时,idxr可能会变大,如果变大了idxr向右移即可。同样是利用gcd的单调性来进行双指针。
g_cnt的代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const int inf=0x3f3f3f3f;
typedef long long ll;
typedef pair<int,int> pii;
typedef unsigned long long ull;
//#define int long long
//const ll P=2281701377;
const ll P=998244353;
const int mod=1e9+7;
#define fi first
#define se second
ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
int n,m;
int a[N];
struct node{
int l,r;
int len;
int g;
ll g_cnt;
vector<pii> pre,ne;
}tr[N*4];
void pushup(node &u,node &l,node &r){
u.len=l.len+r.len;
u.g=gcd(l.g,r.g);
u.g_cnt=0;
u.g_cnt+=(l.g==u.g?l.g_cnt:0);
u.g_cnt+=(r.g==u.g?r.g_cnt:0);
ll sum=0;
for(int i=0;i<r.pre.size();i++)
sum+=r.pre[i].se;
int idx=0;
ll ans=0;
ll t=0;
for(int i=l.ne.size()-1;i>=0;i--){
while(idx<r.pre.size()&&gcd(l.ne[i].fi,r.pre[idx].fi)!=u.g){
//sum-=r.pre[idx].se;
t+=r.pre[idx].se;
idx++;
}
ans+=(ll)(sum-t)*l.ne[i].se;
}
u.g_cnt+=ans;
u.pre=l.pre;
u.ne=r.ne;
for(int i=0;i<r.pre.size();i++){
if(r.pre[i].fi%u.pre.back().fi==0)
u.pre.back().se+=r.pre[i].se;
else
u.pre.push_back({gcd(u.pre.back().fi,r.pre[i].fi),r.pre[i].se});
}
for(int i=0;i<l.ne.size();i++){
if(l.ne[i].fi%u.ne.back().fi==0)
u.ne.back().se+=l.ne[i].se;
else
u.ne.push_back({gcd(u.ne.back().fi,l.ne[i].fi),l.ne[i].se});
}
}
void build(int u,int l,int r){
tr[u]={l,r};
tr[u].g_cnt=0;
tr[u].pre.clear();
tr[u].ne.clear();
if(l==r){
tr[u]={l,r,1,a[l],1};
tr[u].pre.push_back({a[l],1});
tr[u].ne.push_back({a[l],1});
return;
}
int mid=(l+r)>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}
node query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r){
return tr[u];
}
int mid=tr[u].l+tr[u].r>>1;
if(r<=mid) return query(u<<1,l,r);
else if(l>mid) return query(u<<1|1,l,r);
else{
node no1=query(u<<1,l,r);
node no2=query(u<<1|1,l,r);
node ans;
pushup(ans,no1,no2);
return ans;
}
}
int cas=0;
void solve(){
cout<<"Case #"<<++cas<<":"<<endl;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
build(1,1,n);
cin>>m;
while(m--){
int l,r;
cin>>l>>r;
node no=query(1,l,r);
ll ans1=no.g;
ll ans2=no.g_cnt;
cout<<ans1<<' '<<ans2<<endl;
}
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
cin>>t;
while(t--){
solve();
}
}