Vasya and Good Sequences (Codeforces Round #512) 后缀和
嘛,蒟蒻第一次写blog,也是第一次用c++好好写程序,多多关照吧
原题:
Vasya可以将一个正整数的二进制表示中的任两个数位调换位置,并且对每个数字都不限次数,最终希望得到一些异或和为0的连续序列
给定n个正整数ai(n<=3e5,1<=ai<=1e18),求满足题意的序列(l,r)个数
分析:
由于调换不限次数,易知只要统计每个数二进制下1的个数,并且目标序列只要满足以下两个条件即可:
①序列中1的个数和为偶数
②序列中1最多的那个数不能比其他所有数1的个数加起来多
容易想到①通过后缀和操作(勉强算是dp?)可以O(n)完成,但是②当时卡了笔者一会,朴素的O(n^2)显然会TLE
而后注意到每个ai都至少是1,而ai换成二进制最多也就60个1,因此只要序列长度>=61就已经自动满足②了
设ai的上限为M,则总体复杂度为O(n*log(M))
#include <iostream>
#include <cmath>
#include <memory.h>
#include <bitset>
using namespace std;
const int MAXN=10+3e5;
int n,m,i,j,k,t,a[MAXN],f[MAXN][2];
long long x,ans=0;
int main()
{
memset(f,0,sizeof(f));
cin>>n;
for (i=1;i<=n;i++)
{
cin>>x;
t=0;
while (x>0)
{
t+=x&1;
x=x>>1;
}
a[i]=t;
}
f[n+1][0]=0;
f[n+1][1]=0;
for (i=n;i>=1;i--)
{
if (a[i]&1)
{
f[i][0]=f[i+1][1];
f[i][1]=1+f[i+1][0];
}
else
{
f[i][1]=f[i+1][1];
f[i][0]=1+f[i+1][0];
}
}
for (i=1;i<=n;i++)
{
t=a[i];
m=a[i];
k=min(n,i+61);
for (j=i+1;j<=k;j++)
{
t+=a[j];
m=max(m,a[j]);
if ((t>=m*2) and (t%2==0)) ans++;
}
if (t%2) ans+=f[j][1];
else ans+=f[j][0];
}
cout<<ans;
return 0;
}