旋转子段

旋转子段( \(\star\star \))

  • 时限:\(1s\) 内存:\(256M\)

Descrption

  • \(ZYL\)\(N\) 张牌编号分别为 \(1, 2,……,N\)。他把这 \(N\) 张牌打乱排成一排,然后他要做一次旋转使得旋转后固定点尽可能多。
  • 如果第 \(i\) 个位置的牌的编号为 \(i\),我们就称之为固定点。旋转可以被认为是将其中的一个子段旋转 \(180\) 度,这意味着子段的第一张牌和最后一张牌交换位置,以及第二张牌和倒数第二张牌交换位置,等等。写一个程序,找到旋转子段(子段长度可以为 \(1\))。

Input

  • 第一行包含一个整数 \(N (1 ≤ N ≤100 000)\)
  • 第二行有 \(N\) 个数,第 \(i\) 个数表示旋转之前第 \(i\) 个位置的牌的编号。

Output

  • 找到固定点最多的旋转所选的子段,输出旋转之后固定点的个数。

Sample Input

rotate.in
4
3 2 1 4
rotate.out
4

Sample Output

rotate.in
2
1 2
rotate.out
2

Hint

  • 样例解释

    在样例 \(1\) 中,只需要旋转的子段 \([3,2,1]\),将排列变成 \(1\ 2\ 3\ 4\),旋转后所有的牌都为固定点。答案为 \(4\)

    在样例 \(2\) 中,所有的牌已经在固定点,旋转子段 \([1]\) 或者子段 \([2]\),答案为 \(2\)

    数据范围

    \(30\%\) 的数据满足:\(N ≤ 500\)

    \(60\%\) 的数据满足:\(N ≤ 5000\)

    \(100\%\) 的数据满足:\(1 ≤ N ≤ 500 000\)

  • 来源:

分析

  • \(30\) 分很好求, \(n^3\) 暴力枚举即可,\(60\) 分也很好想,\(n\) 枚举旋转区间的中心,\(n\) 从小到大枚举旋转区间长度,\(100\) 分显然至少是 \(n*long(n)\)
  • 假设最优旋转区间时 \([l,r]\) ,如果 \(a[l]!=r\ \&\&\ a[r]!=l\) ,显然区间 \([l+1,r-1]\)必然是最优旋转区间,依次类推,直到出现 \(a[l]==r\ ||\ a[r]==l\) ,此时旋转区间,端点对答案才有贡献。一个区间如果想成为最优解,那么必须满足左右端点之中最少有一个有反转过后成为不动点。
  • 对于一个任意一个数 \(a[i]\) ,要想通过旋转满足 \(a[i]=i\) ,我们必须旋转的区间为 \([l=min(a[i],i),r=max(a[i],[i])]\) 。只有这个性质,对于区间两端的 \([1,l-1],[r+1,n]\) 的不动点我们可以用前缀和去维护,但区间 \([l,r]\) 之间旋转后有多少个不动点仍需要 \(n\) 的时间效率去求解,整体还是 \(n^2\)
  • 通过样例分析,我们发现一个有趣的性质,对序列:\(3,2,1,4\) 最优旋转区间为 \([1,3]\) ,旋转后区间内有 \(3\) 个固定点,我们发现这三个固定点都满足 :\(a[i]+i==4\) ,即满足:值+下标 相等,我们发现如果满足 \(a[i]+i==a[j]+j\) 的点,我们以 \(a[i]\)\(a[j]\) 为端点去旋转区间,肯定能同时满足 \(a[i],a[j]\) 同时成为固定点。进一步发现 如果我们以翻转的区间大小进行排序,如果区间大必然包含区间小的,所以对 \(a[i]+i\) 相同的点我们以旋转区间从小到大进行排序。
  • \(p=a[i]+i\) ,对 \(p\) 相同的如何求其旋转区间呢?显然, \(a[i]\) 为端点的旋转区间为:\([l=min(a[i],i),r=max(a[i],[i])]\) ,区间长度为 :\(len=abs(a[i]-i+1)\Rightarrow len=abs(p-2*i+1)\)
  • 实现步骤:
    1. 预处理出 \(L[i],R[i]\) 分别表示 \(1\sim i\)\(i\sim n\) 区间的不动点个数。
    2. \(vector\)\(p=a[i]+i\) 相同的点封装在同一个桶里,值为 \(i\) ,对每一个桶按区间大小升序排序。
    3. 具体实现见代码。

Code

#include <bits/stdc++.h>
const int maxn=5e5+5;
int L[maxn],R[maxn],a[maxn];
int n,p;
std::vector<int> q[maxn<<1];
bool Cmp(int x,int y){
    return abs((x<<1)-p)<abs((y<<1)-p);
}
void Init(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%d",&a[i]);
        L[i]=(a[i]==i) ? L[i-1]+1:L[i-1];
        q[a[i]+i].push_back(i);
    }
    for(int i=n;i>0;i--)
        R[i]=(a[i]==i)? R[i+1]+1 : R[i+1];
}
void Solve(){
    int ans=0;
    for(int i=2;i<=(n<<1);++i){
        if(!q[i].empty()){
            p=i;
            std::sort(q[i].begin(),q[i].end(),Cmp);
            for(int j=0;j<q[i].size();++j){
                int l=q[i][j];
                int r=i-l;
                if(l>r)std::swap(l,r);
                ans=std::max(ans,L[l-1]+R[r+1]+j+1);
            }
        }
    }
    printf("%d\n",ans);
}
int main(){
    Init();
    Solve();
    return 0;
}
posted @ 2020-08-05 13:24  ♞老姚♘  阅读(272)  评论(0编辑  收藏  举报