10.12T1 寻找性质

Description

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

Input

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

Output

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

Sample Input

4 3 2 1 4

Sample Output

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

Hint

【数据范围】
30%的数据满足:N ≤ 500;
60%的数据满足:N ≤ 5000;
100%的数据满足:1 ≤ N ≤ 100 000。
 
 
 
 
官方题解摆上:

算法一:对于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)。

简明一点就是[i,a[i]]一定是我们的答案区间,然后查询前后的区间没有翻转的个数可以前缀和水掉,至于中间的我们在读入的时候可以vector记录一下i+a[i]这个值有多少个位置是它,然后在枚举的时候就可以二分vector里面的位置就可以了(我tm强行多带了一个n导致50分,人傻自带一个n)

code:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<vector>
 4 #include<algorithm>
 5 #define  N 1000005
 6 using namespace std;
 7 int a[N],s[N];
 8 vector<int>check[1000005];
 9 int main(){
10     int n;
11     cin>>n;
12     for(int i=1;i<=n;i++){
13         cin>>a[i];
14         s[i]=s[i-1]+(a[i]==i?1:0);
15         check[a[i]+i].push_back(i);
16     }
17     int max0=1;
18     if(s[n]==n){
19         cout<<n;
20         return 0;
21     }
22     for(int i=1;i<=n;i++){
23         if(a[i]==i)continue;
24         int temp=0;
25         temp+=s[min(i,a[i])-1]+s[n]-s[max(i,a[i])];
26         int now=a[i]+i;
27         int l=lower_bound(check[now].begin(),check[now].end(),min(i,a[i]))-check[now].begin();
28         int r=upper_bound(check[now].begin(),check[now].end(),max(i,a[i]))-check[now].begin();
29         max0=max(max0,r-l+temp);
30     }
31     cout<<max0;
32     return 0;
33 } 

over

原文地址:https://www.cnblogs.com/saionjisekai/p/9790370.html