EOJ Monthly 2019.5 (based on May Selection)-D.翻转(前缀和处理,思维考虑)

单点时限: 5.0 sec

内存限制: 512 MB

QQ小方以前不会翻转数列,现在他会了,所以他急切的想教会你。

翻转数列指的是,把一个数列倒置。具体来说,如果一个数列是 a1,a2,a3⋯an ,那么翻转以后的数列是 a′1,a′2,a′3⋯a′n=an,an−1,an−2⋯a1 。

单单讲给你听肯定是不够的,为了表现自己,QQ小方现在要考考你。

现在QQ小方有一个数列 a1,a2,a3⋯an 。他想要翻转数列中的其中一个子数列(一个数列的子数列指的是任意个连续的数组成的子序列,子序列可以为空)。而且他希望在翻转以后有尽量多的位置满足 i=a′i 。QQ小方想知道,经过一次翻转操作,数列中最多能有多少位置满足 i=a′i 。

输入格式

输入数据第一行包含一个整数 n ( 1≤n≤5⋅106 ) ,表示数列的长度。

接下来的一行包含 n 个用空格隔开的整数,分别表示 a1,a2,a3⋯an ( 1≤ai≤n ) ,表示初始的数列。

输出格式

输出一行包含一个整数,表示答案。

样例

Input

4
3 4 1 2

Output

2

提示

可以选择翻转 [1,3] 子数列,变成 1,4,3,2 ,则位置 1 和 3 满足要求了。

也可以选择翻转 [2,4] 子数列,变成 3,2,1,4 ,则位置 2 和 4 满足要求了。

没有更优的翻转方案了。

思路:这个题目的话,比赛的时候没做出来,当时想的是,如果他有5e6个点,全都有互相匹配的的话,就只有2.5e3个匹配点了,然后后续暴力就行了,但是但是没做出来,没考虑到那种两边的情况,一开始搞的那个最大值也是不对的,因为一开始就有放好了的,这是我昨天犯的错误,

---------下面是思路-----------------

就是给出的点,有的是本来的位置就是对应的,也就是说我不给他翻转他的位置就是有贡献的,这一点要考虑到,昨天也是错误在这个地方,

下面就是对于每一个本来位置不对应的点的处理,我们把每一个位置不对应的点处理,我们知道,每一个位置不对应的点都会产生一个可能可行的翻转区间,那么我们把这个翻转区间的翻转轴保存,翻转的两端也保存,然后处理每一个翻转轴就可以了,

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=5e6+10;
int n;
int a[maxn],pre[maxn];
struct Node //左右端点和长度
{
    int l,r,len;
    Node(){}
    Node(int x,int y,int z):l(x),r(y),len(z){}
};
int cmp(Node a,Node b)
{
    return a.len<b.len;
}
vector<Node >G[maxn*2];//长度乘二
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        pre[i]=pre[i-1]+(a[i]==i);//记录前缀的本来位置对应好了的数量
        if(a[i]>i)
        {
            G[a[i]+i].push_back(Node(i,a[i],a[i]-i+1));
        }
        else if(a[i]<i)
        {
            G[a[i]+i].push_back(Node(a[i],i,i-a[i]+1));
        }
        else
            G[a[i]+i].push_back(Node(a[i],i,0));
    }
    int maxx=pre[n];
    for(int i=1;i<=2*n;i++)
    {
        sort(G[i].begin(),G[i].end(),cmp);
    }
    for(int i=1;i<=2*n;i++)
    {
        for(int j=0;j<G[i].size();j++)
        {
            Node tmp=G[i][j];
            int qans=pre[n]-pre[tmp.r]+pre[tmp.l-1];//处理本来就对应好了的
            qans+=(j+1);//加上在这个区间内可行的对应端点
            maxx=max(maxx,qans);
        }
    }
    printf("%d\n",maxx);
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值