题目描述

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


输入格式

第一行包含一个整数$N$。
第二行有$N$个数,第$i$个数表示旋转之前第$i$个位置的牌的编号。


输出格式

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


样例

样例输入1:

4
3 2 1 4

样例输出1:

4

样例输入2:

2
1 2

样例输出2:

2


数据范围与提示

样例解释:

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

数据范围:

$30\%$的数据满足:$N\leqslant 500$;
$60\%$的数据满足:$N\leqslant 5,000$;
$100\%$的数据满足:$1\leqslant N\leqslant 500,000$。


题解

$30\%$算法:

裸的暴力,直接搞就行了。

可以用$reverse$卡常,但是没什么用。

注意可以不旋转。

时间复杂度:$\Theta(n^3)$。

期望得分:$30$分。

实际得分:$35$分。

$60\%$算法:

枚举旋转中心和旋转半径,思想类似莫队。

时间复杂度:$\Theta(n^2)$。

期望得分:$60$分。

实际得分:$60$分。

$100\%$算法:

先来利用反证法来证明一个问题:如果最优翻转区间是$[l,r]$,那么如果$a[l]\neq r\&\&a[r]\neq l$,则翻转这个区间$a[l]$和$a[r]$一定都不能成为不动点,对答案一定不能做出贡献,选择区间$[l+1,r-1]$一定不劣。

所以,我们要找的区间一定满足$a[l]=r||a[r]=l$,也就是说,翻转之后一定有一个点成为不动点。

根据上面的出的结论,我们还可以知道,答案一定在所有的$[\min(a[i],i),\max(a[i],i)]$中产生。

显然仅仅是这样我们还是会超时,那么考虑如何进行优化。

依次枚举$n$必不可少,我们考虑从查询中入手。

我们还可以知道,一个点在翻转之后成为不动点,在翻转之前一定满足$a[i]+i=a[j]+j$(式中$i,j$分别表示旋转之前的位置和旋转之后的位置)。

可以使用$vector$优化空间复杂度。

时间复杂度:$\Theta(n\log n)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

$30\%$算法:

#include<bits/stdc++.h>
using namespace std;
int a[500001];
int ans;
int main()
{
int n;
scanf("%d",&n);
for(register int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(a[i]==i)ans++;
}
for(register int i=1;i<n;i++)
for(register int j=i+1;j<=n;j++)
{
register int sum=0;
reverse(a+i,a+j+1);
for(register int k=1;k<=n;k++)
if(a[k]==k)sum++;
reverse(a+i,a+j+1);
ans=max(ans,sum);
}
printf("%d",ans);
return 0;
}

$60\%$算法:

#include<bits/stdc++.h>
using namespace std;
int n,ans,now,res,a[500001];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(a[i]==i)res++;
}
ans=res;
for(int i=1;i<=n;i++)
{
now=res;
for(int j=1;j<=n;j++)
{
int l=i-j,r=i+j;
if(l<=0||r>=n)break;
if(a[l]==r)now++;
if(a[r]==l)now++;
if(a[l]==l)now--;
if(a[r]==r)now--;
ans=max(ans,now);
}
now=res;
for(int j=1;j<=n;j++)
{
int l=i-j+1,r=i+j;
if(l<=0||r>=n)break;
if(a[l]==r)now++;
if(a[r]==l)now++;
if(a[l]==l)now--;
if(a[r]==r)now--;
ans=max(ans,now);
}
}
printf("%d\n",ans);
return 0;
}

$100\%$算法:

#include<bits/stdc++.h>
using namespace std;
int n;
int a[2000000],sum1[2000000],sum2[2000000];
int flag,ans;
vector<int> g[2000000];
bool cmp(int x,int y){return abs((x<<1)-flag)<abs((y<<1)-flag);}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
g[a[i]+i].push_back(i);
if(a[i]==i)sum1[i]=sum2[i]=1;
sum1[i]+=sum1[i-1];
}
for(int i=n;i;i--)sum2[i]+=sum2[i+1];
for(int i=2;i<=(n<<1);i++)
if(!g[i].empty())
{
flag=i;
sort(g[i].begin(),g[i].end(),cmp);
for(int j=0;j<g[i].size();j++)
{
int l=g[i][j];
int r=i-g[i][j];
if(l>r)swap(l,r);
ans=max(ans,sum1[l-1]+sum2[r+1]+j+1);
}
}
cout<<ans<<endl;
return 0;
}

rp++

[CSP-S模拟测试]:旋转子段(数学)的更多相关文章

  1. [CSP-S模拟测试]:不等式(数学)

    题目描述 小$z$热衷于数学.今天数学课的内容是解不等式:$L\leqslant S\times x\leqslant R$.小$z$心想这也太简单了,不禁陷入了深深的思考:假如已知$L,R,S,M$ ...

  2. [CSP-S模拟测试]:A(数学)

    题目传送门(内部题44) 输入格式 一行四个整数,分别表示$S,T,a,b$. 输出格式 输出最小步数,数据保证有解. 样例 样例输入: 10 28 4 2 样例输出: 数据范围与提示 样例解释: 先 ...

  3. [CSP-S模拟测试]:装饰(数学)

    题目传送门(内部题147) 输入格式 每个测试点第一行一个正整数$T$,表示该测试点内的数据组数. 接下来$T$行,每行三个非负整数$a,b,c$,含义如题目中所示. 输出格式 对每组数据输出一行一个 ...

  4. [CSP-S模拟测试]:最大值(数学+线段树)

    题目背景 $Maxtir$最喜欢最大值. 题目传送门(内部题128) 输入格式 第$1$行输入四个正整数$n,m,q$. 第$2$至$n+1$行中,第$i+1$行输入魔法晶石$i$的三种属性$(x_i ...

  5. [CSP-S模拟测试]:求和(数学)

    题目传送门(内部题107) 输入格式 一行五个正整数$x_1,y_1,x_2,y_2,m$ 输出格式 输出一个整数,为所求的答案对$m$取模后的结果. 样例 样例输入: 2 1 5 3 10007 样 ...

  6. [CSP-S模拟测试]:数列(数学)

    题目传送门(内部题95) 输入格式 第一行三个整数$n,a,b$,第二行$n$个整数$x_1\sim x_n$表示数列. 输出格式 一行一个整数表示答案.无解输出$-1$. 样例 样例输入:2 2 3 ...

  7. [CSP-S模拟测试]:Walker(数学)

    题目传送门(内部题86) 输入格式 第一行$n$接下来$n$行,每行四个浮点数,分别表示变换前的坐标和变换后的坐标 输出格式 第一行浮点数$\theta$以弧度制表示第二行浮点数$scale$第三行两 ...

  8. [CSP-S模拟测试]:Six(数学)

    题目传送门(内部题85) 输入格式 一个正整数$N$. 输出格式 一个数表示答案对$1000000007$取模后的结果 样例 样例输入1: 样例输出1: 样例输入2: 样例输出2: 样例输入3: 样例 ...

  9. [CSP-S模拟测试]:Smooth(数学)

    题目传送门(内部题84) 输入格式 两个整数$B,K$ 输出格式 一个整数表示答案 样例 样例输入: 5 100 样例输出: 数据范围与提示 对于$40\%$的数据,保证答案小于$10^7$对于另$2 ...

随机推荐

  1. Python工具库(感谢backlion整理)

    漏洞及渗透练习平台: WebGoat漏洞练习平台: https://github.com/WebGoat/WebGoat webgoat-legacy漏洞练习平台: https://github.co ...

  2. herizai_CD2所做答案

    //herizai_CD1第一题 #include<iostream> #include<iomanip> using namespace std; void print1(i ...

  3. Vue 基础 day02

    Vue Devtools 安装 https://chrome.google.com/webstore/search/vue%20devtools?hl=zh-CN 需要翻墙 过滤器 概念: Vue.j ...

  4. [Git] 004 初识 Git 与 GitHub 之查看历史记录

    在 GitHub 的 UI 界面使用 Git 查看历史纪录 1. 右侧有个 history 2. 点击后跳转页面 3. 点击相应标题(commit 时写的),进入相应版本(历史) 4. 我选择了 I ...

  5. [Git] 003 初识 Git 与 GitHub 之加入文件 第二弹

    在 GitHub 的 UI 界面使用 Git 往仓库里加文件 第二弹 1. 选择已有的文件,点击右侧的 edit 2. 在文件中继续写入文字 小发现:我只写到第 6 行,commit 后再点进去,发现 ...

  6. MySQL学习笔记(上)

    在进行SQL注入原理的剖析的时候,对MySQL数据库掌握薄弱,参照菜鸟教程的MySQL教程速刷一遍MySQL 关于MySQL MySQL是最流行的关系型数据库管理系统,在WEB方面MySQL是最好的R ...

  7. MongoDB 基本操作(增改删)

    1.插入数据 和关系型数据库一样,增加数据记录可以使用insert语句,这是很简单的. 当插入数据时,如果此集合不存在,则MongoDB系统会自动创建一个集合,即不需要刻意预先创建集合 每次插入数据时 ...

  8. Nginx负载均衡的max_fails和fail_timeout的默认配置问题

    今天发现一个奇怪的现象,前端请求后端服务多次后会超时一次,经过多次验证确定是大概10s左右就会超时一次,检查后端服务,发现其中一个节点已经夯死. 但是我们的nginx负载均衡策略是轮询机制,按照配置来 ...

  9. [2019杭电多校第二场][hdu6599]I Love Palindrome String(回文自动机&&hash)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6599 题目大意为求字符串S有多少个子串S[l,r]满足回文串的定义,并且S[l,(l+r)/2]也满足 ...

  10. 在Linux上下载和安装AAC音频编码器FAAC

    Linux上FAAC的安装 安装 下载 http://downloads.sourceforge.net/faac/faac-1.28.tar.gz 解压 tar zxvf faac-1.28.tar ...