bzoj2741

Description

FOTILE得到了一个长为N的序列A,为了拯救地球,他希望知道某些区间内的最大的连续XOR和。
即对于一个询问,你需要求出max(Ai xor Ai+1 xor Ai+2 ... xor Aj),其中l<=i<=j<=r。
为了体现在线操作,对于一个询问(x,y):
l = min ( ((x+lastans) mod N)+1 , ((y+lastans) mod N)+1 ).
r = max ( ((x+lastans) mod N)+1 , ((y+lastans) mod N)+1 ).
其中lastans是上次询问的答案,一开始为0。

Input

第一行两个整数N和M。
第二行有N个正整数,其中第i个数为Ai,有多余空格。
后M行每行两个数x,y表示一对询问。

Output

 

共M行,第i行一个正整数表示第i个询问的结果。

Sample Input

3 3
1 4 3
0 1
0 1
4 3


Sample Output

5
7
7

HINT



HINT

N=12000,M=6000,x,y,Ai在signed longint范围内。



函数性编程神题。

看dyf神牛题解学会的。

首先提一下,这道题是没有负数的。

然后我们用s[i]表示前i个数的xor和。

那么询问[l,r]里的最大一段的xor和就变成了询问最大的s[i] xor s[j] (i,j∈[l-1,r])。

对于给定的i,我们可以将[l-1,r]的数建一个trie树,然后每次尝试走跟i当前位相反的节点,就可以得到对于i的最大xor和。

这里用到了函数性编程,这个trie树明显是可减的。简单来说就是假设插入了第i个数后trie树入口为ent[i],那么想要访问[l,r]的trie树,就从ent[r]节点进入,所有编号小于ent[l]的节点全部无视即可。

这样我们可以在log级时间内求出对于一个[l,r],某个i的最大xor和。

虽然如此,但是爆搞明显是不可以的。

解决这种问题,很容易想到分块。

我们将sqrt(n)个分为一块,然后可以在log * n^1.5的时间内求出所有块之间的最大xor和,将其保存起来,利用二维st算法用(sqrt(n)log)^2=n log^2的时间内预处理,就可以在O(1)的时间内回答端点在某些连续段的最大xor值。

之后考虑端点在两端不完整块上的情况,暴力枚举即可,明显枚举不超过2*sqrt(n)个点,每次查询花费log时间。

算法总体复杂度为O(n^1.5*log+m*sqrt(n)*log),可以再规定时限内通过所有测试点。


虽然这么说,但是我写的比较悬……14848ms才跑完。


program bzoj2741;
const
  len=110;
var
  l,r,ans,all,tot,n,m,i,j,k,q,p:longint;
  two:array [0..32] of longint;
  ent,s,form:array [0..12001] of longint;
  u:array [0..111,0..111] of longint;
  fin:array [0..121] of longint;
  trie:array [0..500001,0..1] of longint;
  max:array [0..11,0..111,0..11,0..111] of longint;

procedure swap (var a,b:longint);inline;
begin
  if a=b then exit;
  b:=a xor b;
  a:=a xor b;
  b:=a xor b;
end;

procedure add (x,last,now:longint);
var
  i,k:longint;
begin
  for i:=30 downto 0 do
    begin
      k:=x and two[i];
      if k<>0 then k:=1;
      if last<>-1 then
        begin
          trie[now]:=trie[last];
          inc(tot);
          trie[now,k]:=tot;
          now:=tot;
          if trie[last,k]=0 then
            last:=-1
                            else
            last:=trie[last,k];
        end
                  else
        begin
          inc(tot);
          trie[now,k]:=tot;
          now:=tot
        end;
    end;
end;

function find (x,l,now:longint):longint;
var
  ans,i,k:longint;
begin
  if l=0 then inc(l);
  ans:=0;
  for i:=30 downto 0 do
    begin
      k:=x and two[i];
      if k<>0 then k:=1;
      if (trie[now,k xor 1]>=l) then
        begin
          ans:=ans+two[i];
          now:=trie[now,k xor 1];
        end
                              else
        now:=trie[now,k];
    end;
  exit(ans);
end;

function qmax (l,r:longint):longint;
var
  i,k:longint;
begin
  if l>r then swap(l,r);
  i:=trunc(ln(r-l+1)/ln(2));
  k:=0;
  if max[i,l,i,l]>k then
    k:=max[i,l,i,l];
  if max[i,l,i,r-(1 shl i) + 1]>k then
    k:=max[i,l,i,r-(1 shl i) + 1];
  if max[i,r-(1 shl i) + 1,i,l]>k then
    k:=max[i,r-(1 shl i) + 1,i,l];
  if max[i,r-(1 shl i) + 1,i,r-(1 shl i) + 1]>k then
    k:=max[i,r-(1 shl i) + 1,i,r-(1 shl i) + 1];
  exit(k);
end;

begin
  read(n,m);
  for i:=0 to 30 do
    two[i]:=1 shl i;
  for i:=1 to n do
    begin
      read(s[i]);
      s[i]:=s[i] xor s[i-1];
      inc(tot);
      ent[i]:=tot;
      add(s[i],ent[i-1],ent[i]);
    end;
  for i:=0 to n do
    begin
      form[i]:=i div len + 1;
      fin[form[i]]:=i;
    end;
  all:=n div len + 1;
  for i:=0 to n do
    for j:=1 to all do
      begin
        k:=find(s[i],ent[j*len-len],ent[fin[j]]);
        if k>u[form[i],j] then
          u[form[i],j]:=k;
      end;
  for i:=1 to all do
    for j:=1 to all do
      max[0,i,0,j]:=u[i,j];
  i:=0;
  while 1 shl i <= all do
    begin
      for q:=1 to all do
        for p:=1 to all do
          if q + (1 shl i)>all then
            max[i+1,q,0,p]:=max[i,q,0,p]
                               else
          if max[i,q,0,p]>max[i,q+(1 shl i),0,p] then
            max[i+1,q,0,p]:=max[i,q,0,p]
                                                  else
            max[i+1,q,0,p]:=max[i,q+(1 shl i),0,p];
      j:=0;
      while 1 shl j<= all do
        begin
          for q:=1 to all do
            for p:=1 to all do
              if p+(1 shl j)>all then
                max[i+1,q,j+1,p]:=max[i+1,q,j,p]
                                 else
              if max[i+1,q,j,p]>max[i+1,q,j,p+(1 shl j)] then
                max[i+1,q,j+1,p]:=max[i+1,q,j,p]
                                                         else
                max[i+1,q,j+1,p]:=max[i+1,q,j,p+(1 shl j)];
          inc(j);
        end;
      inc(i);
    end;
  ans:=0;
  for j:=1 to m do
    begin
      read(l,r);
      l:=(int64(l)+ans) mod n + 1;
      r:=(int64(r)+ans) mod n + 1;
      if l>r then swap(l,r);
      dec(l);
      ans:=0;
      if form[l]=form[r] then
        begin
          for i:=l to r do
            begin
              k:=find(s[i],ent[l],ent[r]);
              if k>ans then ans:=k;
            end;
          writeln(ans);
          continue;
        end;
      if form[l]+1<=form[r]-1 then
        ans:=qmax(form[l]+1,form[r]-1);
      for i:=l to fin[form[l]] do
        begin
          k:=find(s[i],ent[l],ent[r]);
          if k>ans then ans:=k;
        end;
      for i:=form[r]*len-len to r do
        begin
          k:=find(s[i],ent[l],ent[r]);
          if k>ans then ans:=k;
        end;
      writeln(ans);
    end;
end.


BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值