题目链接:https://ac.nowcoder.com/acm/contest/95323/K
题意:
给定一个长度为n的数组,求所有[l,r]区间xor等于区间gcd的个数(l<r)
思路:
不妨固定左端点l,a[l]=x,发现右端点在扩增的时候,区间gcd最多变化logx次,因此可以二分出区间gcd的变化点p
同时用ST表求出区间[p1,p2]gcd大小,设为g,那么区间pre[r] xor pre[l-1] = g => pre[r]= g xor pre[l-1]
(其中r属于[p1,p2]且r不能为l),通过map套vector上二分能求出这段区间中pre[r]的数的个数
int Log[maxn];
void init(){Log[1]=0;for(int i=2;i<=2e5;i++){Log[i] =Log[i/2]+1;}
}void solve(){int n;cin>>n;vector<int>a(n+1);rep(i,1,n)cin>>a[i];vector<vector<int>>f(n+1,vector<int>(27));rep(i,1,n)f[i][0]=a[i];rep(j,1,26){for(int i=1;i+(1ll<<j)-1<=n;i++){f[i][j]=__gcd(f[i][j-1],f[i+(1ll<<j-1)][j-1]);}}auto query =[&](int l,int r)->int{int k=Log[r-l+1];return __gcd(f[l][k],f[r-(1ll<<k)+1][k]);};vector<int>pre(n+1);rep(i,1,n){pre[i]=(pre[i-1]^a[i]);}map<int,vector<int>>cnt;rep(i,1,n)cnt[pre[i]].pb(i);int ans=0;auto cal=[&](int x,int lt,int rt)->int{auto itl=lower_bound(cnt[x].begin(),cnt[x].end(),lt);int left=0;if(itl==cnt[x].end())return 0;else left=lower_bound(cnt[x].begin(),cnt[x].end(),lt)-cnt[x].begin();auto itr=upper_bound(cnt[x].begin(),cnt[x].end(),rt);if(itr==cnt[x].end()){int m=cnt[x].size();return (m-left);}else{int right=upper_bound(cnt[x].begin(),cnt[x].end(),rt)-cnt[x].begin();return (right-left);}};for(int l=1;l<=n;l++){int L=l,R=n;int u=a[l];while(L<=n){int tmp=L;int ed=-1;while(L<=R){int mid = L+R>>1;if(query(l,mid)==u){L=mid+1;ed=mid;}else R=mid-1;}if(ed==-1)break;int x= (pre[l-1]^u);ans += cal(x,max(l+1,tmp),ed);if(ed+1>n)break;u=query(l,ed+1);L=ed+1;R=n;}}cout<<ans<<endl;
}