[题解] P3794 签到题IV
\color{red}博客内食用效果更佳(点我)
思路:二分+ST 表
复杂度:O(n\log^3{n})
主体思路
首先考虑暴力做法,枚举左右端点,暴力算区间
注意到一个常见的性质,连续段的
因为
代码实现需要注意的地方:
^运算符优先级低于==运算符,需要加括号。- 开 long long。
- 注意初值设置。
参考代码:
代码中以字母
详见注释。
#include<bits/stdc++.h>
#define LL long long
#define UN unsigned
using namespace std;
//--------------------//
//IO
inline int rd()
{
int ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
//--------------------//
const int N=5e5+5,LG=30;
int n,k;
int a[N];
struct ST
{
int lg[N];
int stg[N][LG],sto[N][LG];
void init()
{
for(int i=2;i<=n;i++)
lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;i++)
stg[i][0]=sto[i][0]=a[i];
for(int j=1;j<=lg[n];j++)
{
for(int i=1;lg[n-i+1]>=j;i++)
{
stg[i][j]=__gcd(stg[i][j-1],stg[i+(1<<(j-1))][j-1]);
sto[i][j]=sto[i][j-1]|sto[i+(1<<(j-1))][j-1];
}
}
return;
}
int qg(int l,int r)
{
int lgl=lg[r-l+1];
return __gcd(stg[l][lgl],stg[r-(1<<lgl)+1][lgl]);
}
int qo(int l,int r)
{
int lgl=lg[r-l+1];
return sto[l][lgl]|sto[r-(1<<lgl)+1][lgl];
}
}S;//ST 表部分
int findg(int l,int r,int pos)//二分部分
{
int res=0,val=S.qg(r,pos);
while(l<=r)
{
int mid=l+r>>1;
if(S.qg(mid,pos)==val)
r=mid-1;
else
res=mid,l=mid+1;
}
return res;
}
int findo(int l,int r,int pos)
{
int res=0,val=S.qo(r,pos);
while(l<=r)
{
int mid=l+r>>1;
if(S.qo(mid,pos)==val)
r=mid-1;
else
res=mid,l=mid+1;
}
return res;
}
int tcnt,tem[N];
LL ans;
void work()
{
for(int tp,i=1;i<=n;i++)
{
tem[tcnt=1]=tp=i;//对于右端点我们需要存为第一个左端点
while(true)
{
tp=findg(1,tp,i);
tem[++tcnt]=tp;
if(tp==0)
break;
}
tp=i;
while(true)
{
tp=findo(1,tp,i);
tem[++tcnt]=tp;
if(tp==0)
break;
}
sort(tem+1,tem+tcnt+1);
tcnt=unique(tem+1,tem+tcnt+1)-tem-1;
for(int x,xg,xo,j=tcnt;j>1;j--)
{
x=tem[j],xg=S.qg(x,i),xo=S.qo(x,i);
if((xo^xg)==k)
ans+=tem[j]-tem[j-1];//这一段长度是这一段的贡献
}
}
}
//--------------------//
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
S.init();
work();
printf("%lld",ans);
return 0;
}