翻转数列

Posted by-w

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了翻转数列相关的知识,希望对你有一定的参考价值。

某个小朋友要题解,那我也就发一份吧(反正自己又一次翻车了 QAQ)

T1题目大意是给你一个数列,你能将其中的一段区间翻转,要求这个区间翻转后,整个数列的数在自己位子上的数最多,例如数字1在1位置上,求这个区间两端元素。(n<=500000)

这道题第一眼就可以看出是一题贪心,枚举每个点或不翻转,肯定是不行的。

那我们通过思考,发现将这个区间一定将某些数翻转到它应该在的位置上,于是可以计算出每个点若是将它翻转到自己应该在的位置所依靠的对称中心

我们对每个翻转中心计数即可,但是发现翻转一个区间后可能会使得一些原来有序的数改变位置,也就是说只要使得序列的扩张是从小到大的(好像说不清楚啊QAQ)

所以根据每一个点我们计算出将它翻转到自己应该在的位置的翻转中心,之后先根据翻转中心排序(这里可能会出现翻转中心在两数之间所以我们不对它除以2)

对于相同的翻转中心我们根据它的区间长度从小到大排序,使得区间从小到大扩张,因为这个区间一定是从上一个更小的区间扩张而来的,所以我们可以直接统计该区间内的价值,

对于区间之外的可以直接前缀和处理

代码在此(风格诡异,敬请谅解)

var
i,j,ans,k,max,l1,mid,n,k1,k2:longint;
lw,a,w,sum,last,s:array[0..5000000] of longint;
    procedure sort1(l,r: longint);
      var
         i,j,x,y: longint;
      begin
         i:=l;
         j:=r;
         x:=lw[(l+r) div 2];
         repeat
           while lw[i]<x do
            inc(i);
           while x<lw[j] do
            dec(j);
           if not(i>j) then
             begin
                y:=lw[i]; lw[i]:=lw[j]; lw[j]:=y;
                y:=a[i]; a[i]:=a[j]; a[j]:=y;
                y:=w[i]; w[i]:=w[j]; w[j]:=y;
                inc(i);
                j:=j-1;
             end;
         until i>j;
         if l<j then
           sort1(l,j);
         if i<r then
           sort1(i,r);
      end;
    procedure sort(l,r: longint);
      var
         i,j,x,y: longint;
      begin
         i:=l;
         j:=r;
         x:=w[(l+r) div 2];
         repeat
           while w[i]<x do
            inc(i);
           while x<w[j] do
            dec(j);
           if not(i>j) then
             begin
                y:=a[i]; a[i]:=a[j]; a[j]:=y;
                y:=w[i]; w[i]:=w[j]; w[j]:=y;
                y:=lw[i]; lw[i]:=lw[j]; lw[j]:=y;
                inc(i); j:=j-1;
             end;
         until i>j;
         if l<j then
           sort(l,j);
         if i<r then
           sort(i,r);
      end;
procedure gg;
var i:longint;
begin
  for i:=1 to n do
  begin
    sum[i]:=sum[i-1];
    if a[i]=i then sum[i]:=sum[i]+1;
  end;
  for i:=n downto 1 do
  begin
    last[i]:=last[i+1];
    if a[i]=i then last[i]:=last[i]+1;
  end;
end;
begin
  readln(n);
  for i:=1 to n do
  begin
    read(a[i]);
    mid:=i+a[i];
    if a[i]<=i then lw[i]:=a[i]
    else lw[i]:=i;
    w[i]:=mid;
    s[i]:=a[i];
  end;
  gg;
  sort(1,n);
  i:=1; l1:=0;
  while i<=n do
  begin
    inc(l1);
    if (w[i]<>w[i+l1]) or (i+l1>n) then
    begin
      sort1(i,i+l1-1); ans:=0;
     // writeln(i,‘ ‘,i+l1-1);
      for j:=i+l1-1 downto i do
      begin
        ans:=ans+1;
        //writeln(j,‘ ‘,ans);
        if ans+sum[lw[j]-1]+last[w[j]-lw[j]+1]>max then
        begin
          max:=ans+sum[lw[j]-1]+last[w[j]-lw[j]+1]; 
          k1:=lw[j]; 
          k2:=w[j];
        end;
      end;
      i:=i+l1; l1:=0;
    end;
  end;
  {writeln;
  for i:=1 to n do
  begin
    writeln(a[i],‘ ‘,lw[i],‘ ‘,w[i],‘ ‘,sum[i],‘ ‘,i);
  end;}
  writeln(s[k1],‘ ‘,s[k2-k1]);
end

以上是关于翻转数列的主要内容,如果未能解决你的问题,请参考以下文章

翻转数列

PTA乙级 (1049 数列的片段和 (20分))

数列和+水仙花数+句子翻转

POJ 3581 Sequence(后缀数组)

1049. 数列的片段和(20)

1049 数列的片段和 (20 分)