• HZOJ 旋转子段


    作者的正解:

    算法一:对于30%的数据: 直接枚举区间直接模拟,时间复杂度O(N3)。

    算法二:对于60%的数据:枚举旋转中心点,然后再枚举旋转的端点, 我们可以用O(n)的预处理求前缀和记录固定点,总时间复杂度O(N2)。

    算法三:对于100%的数据:假设有最优解为[i,j](i,j皆为下标,A[i],A[j]才是题目所要输出的答案)。if(A[i]!=j&&A[j]!=i),就是A[i]和A[j]经过旋转之后都没有成为不动点,那么[i+1,j-1]也是一个最优解(如果i+1>j-1,那么[1,1]也是最优解)。两端旋转之后如果都没成为不动点我们可以去掉两端,不停地去掉后得到最优解为[x,y]则有A[x]=y或者A[y]=x,或者x=y=1( [1,1]可以不参与讨论).

    因此最优解一定是[min(i,A[i]),max(i,A[i])](i=1,2,3......,n)。

    只需要枚举n个值就能找到最优解,由此找最优解问题变成了查询问题。假设i<=A[i],要查询的区间变成了三段[1,i-1]   [i, A[i] ]   [ A[i+1] ,n],固定点个数O(1)的时间查询。中间这一段 [i, A[i] ]经过了一次旋转,对于旋转后的固定点j有A[j]+j=A[i]+i且,而[j,A[j]]也是我们要考虑成为最优解的一个旋转,我们事先将这n种旋转按照轴心不同分类,每一类由旋转区间长度短到长排序。以上算法因为要排序所以复杂度为O(N log N)。

    然而数据很水我们可以直接错解碾标算。

    对于60的数据:

    对于一个旋转中心,他的至少一个端点反转后是固定点,即满足(a[i]+i=mid*2)。于是我们可以枚举这个端点,然后就可以算出旋转中心进而On统计答案。

    然后说一下我的错解(其实很接近正解了):

    根据以上算法可以发现求得就是对于一个旋转中心的旋转段总(a[i]+i=mid*2)的点数以及旋转段外本来就是固定点的个数,前者可以用桶处理,后者可以用前缀和处理。总复杂度O(n)。

    对于旋转段端点的处理,更新桶时处理最后保证对称即可。

     1         for(int i=1;i<=n;i++)
     2         {
     3             sum[i+a[i]]++;
     4             l[i+a[i]]=min(l[i+a[i]],i);
     5             r[i+a[i]]=max(r[i+a[i]],i);
     6         }
     7         for(int i=1;i<=n*2;i++)
     8         {
     9             int tl=l[i],tr=r[i];
    10             l[i]=min(l[i],i-tr);
    11             r[i]=max(r[i],i-tl);
    12         }

     所以到现在你发现这个算法错误的地方吗?没错上面只是处理的对于每个旋转中心最大的旋转段,但是这不一定是最优的(然而数据中好像都是最优的……),提供一组hack数据:

    15

    14 2 8 9 10 11 1 15 6 5 4 3 13 7 12

    正确答案是7,而以上算法出的是6。错误就在于这里最大的旋转区间并不是最优的。

    为了解决这个问题,就需要将最大区间不断收缩寻找最优解。

    但是这样就又成$n^2$的了,怎么办呢?

    其实可以发现一步一步收缩的话有很多步数是没有意义的,我们可以考虑对每个旋转中心开一个vector存a[i]+i=mid的点的位置,按照离旋转中心的距离排序,这样收缩的时候只需枚举vector,以上算法因为要排序所以复杂度为O(N log N)。

      1 #include<iostream>
      2 #include<cstring>
      3 #include<cstdio>
      4 #include<cmath>
      5 #include<ctime>
      6 #define MAXN 500010
      7 #define LL long long
      8 #define ma(x,y) memset(x,y,sizeof(x))
      9 using namespace std;
     10 int n,a[MAXN];
     11 int sum[MAXN*2],l[MAXN*2],r[MAXN*2];
     12 int pre[MAXN];
     13 inline int read();
     14 signed main()
     15 {
     16 //    freopen("in.txt","r",stdin);
     17 //    freopen("tem","r",stdin);
     18 //    freopen("1.out","w",stdout);
     19 
     20     double sst=clock();
     21     n=read();
     22     for(int i=1;i<=n;i++)a[i]=read();
     23 /*    if(n<=500)
     24     {
     25         int ans=0,res=0;
     26         for(int i=1;i<=n;i++)
     27             for(int j=i;j<=n;j++)
     28             {
     29                 for(int k=0;i+k<=j-k;k++)
     30                 {
     31                     if(a[i+k]==j-k)res++;
     32                     if(a[j-k]==i+k)res++;
     33                     if((i+k==j-k)&&a[j-k]==i+k)res--;
     34                 }
     35                 for(int k=1;k<i;k++)
     36                 if(a[k]==k)res++;
     37                 for(int k=j+1;k<=n;k++)
     38                 if(a[k]==k)res++;
     39                 ans=max(ans,res);res=0;
     40             }
     41         printf("%d
    ",ans);
     42         return 0;
     43     }
     44     if(n<=5000)
     45     {
     46         int ans=0,res=0;
     47         for(int i=1;i<=n;i++)
     48         {
     49             res=0;
     50             double mid=1.0*(i+a[i])/2;
     51             int st=min(a[i],i),en=max(a[i],i);
     52             for(int j=st;j<=en;j++)
     53                 if(1.0*(double)(j+a[j])/2==mid)res++;
     54             for(int j=1;j<st;j++)
     55                 if(j==a[j])res++;
     56             for(int j=en+1;j<=n;j++)
     57                 if(j==a[j])res++;
     58             ans=max(ans,res);
     59         }
     60         printf("%d
    ",ans);
     61         return 0;
     62     }
     63     else*/
     64     {
     65         for(int i=1;i<=n;i++)
     66         {
     67             pre[i]=pre[i-1];
     68             if(a[i]==i)pre[i]++;
     69         }
     70         ma(l,0x7f);ma(r,0);
     71         for(int i=1;i<=n;i++)
     72         {
     73             sum[i+a[i]]++;
     74             l[i+a[i]]=min(l[i+a[i]],i);
     75             r[i+a[i]]=max(r[i+a[i]],i);
     76         }
     77         for(int i=1;i<=n*2;i++)
     78         {
     79             int tl=l[i],tr=r[i];
     80             l[i]=min(l[i],i-tr);
     81             r[i]=max(r[i],i-tl);
     82         }
     83         int ans=0;
     84         for(int i=1;i<=n*2;i++)
     85         if(sum[i])
     86         {
     87             ans=max(ans,sum[i]+pre[l[i]-1]+pre[n]-pre[r[i]]);
     88 /*            for(int j=1;j&&l[i]<r[i];j++)
     89             {
     90                 double st=clock();
     91                 if(l[i]+a[l[i]]==i)sum[i]--;
     92                 if(r[i]+a[r[i]]==i)sum[i]--;
     93                 l[i]++,r[i]--;
     94                 ans=max(ans,sum[i]+pre[l[i]-1]+pre[n]-pre[r[i]]);
     95                 double en=clock();
     96                 if((en-st)/1000>5)break;
     97                 if((en-sst)/1000>550)break;
     98             }*/
     99         }
    100         printf("%d
    ",ans);
    101         return 0;
    102     }
    103 }
    104 inline int read()
    105 {
    106     int s=0,f=1;char a=getchar();
    107     while(a<'0'||a>'9'){if(a=='-')f=-1;a=getchar();}
    108     while(a>='0'&&a<='9'){s=s*10+a-'0';a=getchar();}
    109     return s*f;
    110 }
    所以我并没有正解的代码
  • 相关阅读:
    MySQL补充
    不同操作系统下虚拟环境的搭建
    Python导学基础(二)变量与基础数据类型
    Python导学基础(一)介绍
    KM 算法
    题解-CF1065E Side Transmutations
    题解-CF1140E Palindrome-less Arrays
    题解-CF677D Vanya and Treasure
    splay文艺平衡树
    splay区间操作(bzoj1500)
  • 原文地址:https://www.cnblogs.com/Al-Ca/p/11318849.html
Copyright © 2020-2023  润新知