Code Forces 1030E

题目大意

给你n个数,你可以交换一个数的任意二进制位,问你可以选出多少区间经过操作后异或和是0。

思路分析:

根据题目,很容易知道,对于每个数,我们可以无视它的1在那些位置,只要关注它有几个1即可,如果它的1的数量可以通过加减为0,那么这个区间就是合法的。

可以看到,两个数(分别有x,y个1,且x〉y)通过交换位置再进行异或操作,可能获得的新数中1的数量为y-x,y-x+2,y-x+4,...,x+y-2,x+y。

那么对于每一个左端点(l),它的合法右端点(r)的数量为sum[r]-sum[l-1]为偶数的r的个数(sum为前缀和数组)。同时,这个区间还要满足区间内1的数量最多的数(假设它在第k位)要小于sum[k]-sum[l-1]的一半,因为假如说它不满足这一条件的话,那么就没有足够多的1同它相抵消,也就不会合法。

那么我们应该如何避免这种状况呢?

很简单,可能会是不合法右端点的几个点(最多64个)直接暴力做就行了。

代码:

var
  a,sum,odd,even:array[0..300000]of longint;
  i,j,s,max,n,m:longint;
  ans,x:int64;
begin
  read(n);
  for i:=1 to n do
  begin
    read(x);
    while x>0 do
    begin
      a[i]:=a[i]+x and 1;
      x:=x>>1;
    end;
  end;
  for i:=1 to n do
  begin
    sum[i]:=sum[i-1]+a[i];
    odd[i]:=odd[i-1]; even[i]:=even[i-1];
    if sum[i]and 1=1 then inc(odd[i]) else inc(even[i]);
  end;
  for i:=1 to n do
    if sum[i-1]and 1=1 then ans:=ans+odd[n]-odd[i-1]
      else ans:=ans+even[n]-even[i-1];
  writeln(ans);
  for i:=1 to n do
  begin
    if i+64>n then m:=n else m:=i+64;
    s:=0; max:=0;
    for j:=i to m do
    begin
      if a[j]>max then max:=a[j];
      s:=s+a[j];
      if (2*max>s)and(s mod 2=0) then dec(ans);
    end;
  end;
  writeln(ans);
end.

 

posted @ 2018-10-01 19:38  WR_Eternity  阅读(176)  评论(0编辑  收藏  举报