硝基甲苯之魇
题目链接: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;
}

浙公网安备 33010602011771号