翻转数列

周四爆炸(反正自己又一次翻车了 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

转载于:https://www.cnblogs.com/by-w/p/9612488.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值