[CSP-S模拟测试]:旋转子段(数学)
题目描述
$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模拟测试]:旋转子段(数学)的更多相关文章
- [CSP-S模拟测试]:不等式(数学)
题目描述 小$z$热衷于数学.今天数学课的内容是解不等式:$L\leqslant S\times x\leqslant R$.小$z$心想这也太简单了,不禁陷入了深深的思考:假如已知$L,R,S,M$ ...
- [CSP-S模拟测试]:A(数学)
题目传送门(内部题44) 输入格式 一行四个整数,分别表示$S,T,a,b$. 输出格式 输出最小步数,数据保证有解. 样例 样例输入: 10 28 4 2 样例输出: 数据范围与提示 样例解释: 先 ...
- [CSP-S模拟测试]:装饰(数学)
题目传送门(内部题147) 输入格式 每个测试点第一行一个正整数$T$,表示该测试点内的数据组数. 接下来$T$行,每行三个非负整数$a,b,c$,含义如题目中所示. 输出格式 对每组数据输出一行一个 ...
- [CSP-S模拟测试]:最大值(数学+线段树)
题目背景 $Maxtir$最喜欢最大值. 题目传送门(内部题128) 输入格式 第$1$行输入四个正整数$n,m,q$. 第$2$至$n+1$行中,第$i+1$行输入魔法晶石$i$的三种属性$(x_i ...
- [CSP-S模拟测试]:求和(数学)
题目传送门(内部题107) 输入格式 一行五个正整数$x_1,y_1,x_2,y_2,m$ 输出格式 输出一个整数,为所求的答案对$m$取模后的结果. 样例 样例输入: 2 1 5 3 10007 样 ...
- [CSP-S模拟测试]:数列(数学)
题目传送门(内部题95) 输入格式 第一行三个整数$n,a,b$,第二行$n$个整数$x_1\sim x_n$表示数列. 输出格式 一行一个整数表示答案.无解输出$-1$. 样例 样例输入:2 2 3 ...
- [CSP-S模拟测试]:Walker(数学)
题目传送门(内部题86) 输入格式 第一行$n$接下来$n$行,每行四个浮点数,分别表示变换前的坐标和变换后的坐标 输出格式 第一行浮点数$\theta$以弧度制表示第二行浮点数$scale$第三行两 ...
- [CSP-S模拟测试]:Six(数学)
题目传送门(内部题85) 输入格式 一个正整数$N$. 输出格式 一个数表示答案对$1000000007$取模后的结果 样例 样例输入1: 样例输出1: 样例输入2: 样例输出2: 样例输入3: 样例 ...
- [CSP-S模拟测试]:Smooth(数学)
题目传送门(内部题84) 输入格式 两个整数$B,K$ 输出格式 一个整数表示答案 样例 样例输入: 5 100 样例输出: 数据范围与提示 对于$40\%$的数据,保证答案小于$10^7$对于另$2 ...
随机推荐
- GMSSL中生成SM2或RSA1024或RSA2048的证书相关命令
1.生成KEY:gmssl sm2 -genkey -out 01.root.pemgmssl genrsa -out 01.root_plain.key 2048gmssl genrsa -out ...
- 字符串中的TOUPPER函数
std::string& str_toupper(std::string& s) { std::transform(s.begin(), s.end(), s.begin(), []( ...
- Centos7 -samba服务配置
Centos7 -samba服务配置 https://blog.csdn.net/zh515858237/article/details/76914905 http://blog.51cto.com/ ...
- oracle--登陆用户机制
操作系统验证 密码文件验证 数据库验证 1.操作系统验证 sqlplus / as sysdba; 未使用用户和密码登陆 这是操作系统验证,由这个操作系统的组用户创建的,所以这个操作系统可以直接登陆, ...
- Docker 容器化部署1小时简单入门
Docker简介 Docker是DotCloud开源的.可以将任何应用包装在Linux container中运行的工具.2013年3月发布首个版本,当前最新版本为1.3.Docker基于Go语言开发, ...
- [Python3] 007 列表的遍历,你是 for 联盟还是 while 部落
目录 少废话,直接上例子 for 联盟 for 与 list 的简单合作 for 的老搭档 range() for 与嵌套列表(双层列表) for 从 if 那儿认识的 else 循环"三杰 ...
- Spring事务管理之几种方式实现事务(转)
一:事务认识 大家所了解的事务Transaction,它是一些列严密操作动作,要么都操作完成,要么都回滚撤销.Spring事务管理基于底层数据库本身的事务处理机制.数据库事务的基础,是掌握Spring ...
- wikioi 2144 分步二进制枚举+map记录
题目描写叙述 Description 有n个砝码,如今要称一个质量为m的物体,请问最少须要挑出几个砝码来称? 注意一个砝码最多仅仅能挑一次 输入描写叙述 Input Description 第一行两个 ...
- 攻防世界--simple-check-100
测试文件:https://adworld.xctf.org.cn/media/task/attachments/2543a3658d254c30a89e4ea7b8950c27.zip 这道题很坑了, ...
- 本地文件夹上传到Github(一)
1.在要上传的文件夹下单击右键,选择Git Bash here打开Git bash,设置全局用户名和邮箱 语法:git config --global user.name wandou 语法:git ...