題意是,給你一個序列,問有多少區間 [l,r] 滿足 a[l]%a[l+1]%a[l+2].....%a[r]==a[r]%a[r-1]%a[r-2]...%a[l]?
我們考慮一個區間 [l,r] 假設區間內最小值在pos處取得,那麼區間的合法性可以表示爲
a[l]%a[l+1]%a[l+2].....%a[pos]==a[r]%a[r-1]%a[r-2]...%a[pos].因爲在mod a[pos]之後,值是小於apos的,繼續取餘比較大的數也沒有什麼意義。
如果pos==l || pos==r 呢?如果區間內沒有等於apos的數,那麼區間一定是不合法的。只有區間內有多個等於apos的數纔有可能合法,這裏需要特判一下。
所以只要統計一個每個最小值左右相等的數的乘積,注意不要計算重複。
可以分治解決這個問題。我們每次找到區間內最小值(多個取最左邊的),遞歸左區間,遞歸右區間。
對於每一個分治的區間L,R,我們用set去維護所有數向左取模和所有數向右取模。
那麼計算的時候,先分別把左區間向右的mod apos, 右區間向左的mod pos,這裏注意,取模的時候先二分找到第一個大於apos的值,再向後依次取模,這樣保證每次操作都會使一個數變小。(這裏的複雜度是nlognlogn的,因爲一個最多有效取模log次,那麼n個數最多取模nlog次,再算上set的複雜度)。
然後統計答案,合併左右區間。
爲了保證分治的複雜度,就是要啓發式分治嘛。
統計答案的時候要遍歷小的區間,二分大的區間。合併的時候也是把小區間插入到大的區間裏去。
這樣複雜度也是nloglog的嘛。
#include <bits/stdc++.h>
using namespace std;
#define N 200025
#define M 610
#define ll long long
#define mod 1000000007
#define go(i,a,b) for(int i=(a);i<=(b);i++)
#define dep(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
#define inf 0x3f3f3f3f
#define ld long double
#define pii pair<int,int>
#define pdd pair<double,double>
#define vi vector<int>
#define madd(a,b) (a+=(b)%mod)%=mod
#define lowb(c,len,x) lower_bound(c+1,c+len+1,x)-c
#define uppb(c,len,x) upper_bound(c+1,c+len+1,x)-c
#define ls i*2+1
#define rs i*2+2
//#define mid (l+r)/2
#define lson l,mid,ls
#define rson mid+1,r,rs
//#define root 1,cnt,0
#define ms(a,b) memset(a,b,sizeof a)
#define muti int T,cas=1;cin>>T;while(T--)
#define lll __int128
#define si short int
#define fi first
#define se second
#define l(x) (x&-x)
#define G(x) for( int ii=h[x];ii;ii=eg[ii].n )
int mm[2*N],n,las[N];
int dp[2*N][20],a[N],cnt;
set<pii>st[N];
set<pii>::iterator it,it1,itt;
ll ans=0;
void init()
{
mm[0]=-1;
go(i,1,n){
dp[i][0]=i;
mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
}
go(j,1,mm[n])
for(int i=1; i+(1<<j)-1<=n; i++)
dp[i][j]=a[dp[i][j-1]]<=a[dp[i+(1<<(j-1))][j-1]]?dp[i][j-1]:dp[i+(1<<(j-1))][j-1];
}
int quy(int x,int y){
if(x>y)return n+1;
int k=mm[y-x+1];
return a[dp[x][k]]<=a[dp[y-(1<<k)+1][k]]?dp[x][k]:dp[y-(1<<k)+1][k];
}
int join(int x,int y,int z){ //合併兩個set,插入a[mid]
if(st[x].size()>st[y].size())swap(x,y);
//if(!st[x].size())return y;
for(it=st[x].begin();it!=st[x].end();it++){
it1=st[y].lower_bound(pii(it->fi,0));
if(it1==st[y].end()||it1->fi!=it->fi)st[y].insert(*it);
else {
pii tmp=pii(it1->fi,it1->se+it->se);
st[y].erase(it1);
st[y].insert(tmp);
}
}
st[x].clear();
it=st[y].lower_bound(pii(z,0));
if(it!=st[y].end()&&it->fi==z){
pii tmp=pii(it->fi,it->se+1);
st[y].erase(it);
st[y].insert(tmp);
}
else st[y].insert(pii(z,1));
return y;
}
void cal(int x,int y){ //統計答案
if(st[x].size()>st[y].size())swap(x,y);
if(!st[x].size())return ;
for(it=st[x].begin();it!=st[x].end();it++){
it1=st[y].lower_bound(pii(it->fi,0));
if(it1!=st[y].end()&&it1->fi==it->fi)ans+=1ll*(it->se)*(it1->se);
}
}
//void pri(int x){
// cout<<":::::::::::::"<<endl;
// for(it=st[x].begin();it!=st[x].end();it++){
// cout<<it->fi<<'-'<<it->se<<' ';
// }
// cout<<endl<<endl;
//}
void upd(int x,int y){ //對set所有的值 mod a[mid]
for(it=st[x].lower_bound(pii(y,0));it!=st[x].end();){
pii tmp=pii(it->fi%y,it->se);
it1=it;it++;
st[x].erase(it1);
itt=st[x].lower_bound(pii(tmp.fi,0));
if(itt!=st[x].end()&&itt->fi==tmp.fi){
pii tmp1=pii(itt->fi,itt->se+tmp.se);
st[x].erase(itt);
st[x].insert(tmp1);
}
else st[x].insert(tmp);
}
}
pii solve(int l,int r,int fl=0){
if(fl)ans++;
if(l>=r){
if(l>r)return pii(++cnt,++cnt);
//cout<<fl<<endl;
st[++cnt].insert(pii(a[l],1));
st[++cnt].insert(pii(a[l],1));
return pii(cnt-1,cnt);
}
int mid=quy(l,r);
//遞歸左右區間
pii pl=solve(l,mid-1),pr=solve(mid+1,r,a[quy(mid+1,r)]==a[mid]?1:0),res;
//分別取模a[mid]
upd(pr.fi,a[mid]);upd(pl.se,a[mid]);
//特判區間內有多個相同的最小值
it=st[pr.fi].lower_bound(pii(0,0));
if(it!=st[pr.fi].end()&&it->fi==0)ans+=fl*(it->se);
//統計答案
cal(pl.se,pr.fi);
//合併左右區間,a[mid]
res=pii(join(pl.fi,pr.fi,a[mid]),join(pl.se,pr.se,a[quy(mid+1,r)]==a[mid]?0:a[mid]));
return res;
}
int main()
{
cin>>n;
go(i,1,n)scanf("%d",&a[i]);
a[n+1]=inf;
init();
solve(1,n);
cout<<ans+n<<endl;
}
/*
10
10 9 8 7 1 2 3 4 5 6
*/