前言
这次考试竟然出现了许多玄学的情况,请听我下面一一列举…
注:这次考试所有的题目均为 时限:1S 空限:256M
1、无聊的军官(officer.pas/c/cpp)
【问题描述】
每个学年的开始,高一新生们都要进行传统的军训。今年有一个军训教官十分奇怪,他为了测试学员们的反应能力,每次吹哨后学员们都会变换位置。每次左数第i位学员都会站到第ai个位置,经过若干次之后,队伍又会回到原来的样子。
你的任务是计算n个人的队伍至少经过多少次之后,队伍恢复到原来样子。
输入officer.in
输入文件的第一行包含一个整数N( 0≤N≤10000 ),表示队伍的人数。
接下来N行,每行一个正整数ai表示左起第i个人接下来出现在左起第ai个位置上。
输出officer.out
仅包括一行,一个正整数M,表示军官最少的吹哨次数。
【样例输入】
5
2
3
4
5
1
【样例输出】
5
【数据规模】
对于30%的数据,有 N≤100
对于100%的数据,有 N≤10000 ;
对于全部数据,答案在均在64位整数范围之内。
【题解】
这个题目记得好像是一道NOI导刊上的题目(也有可能记错了),总之这道题并不是很难,考场上大家的实际得分也证明如此。
其实这道题目,只需要根据i和ai的关系建立图形,求出整个图中所有的环的长度,再最后求最小公倍数即可。
【代码】
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int size = 10000+10;
bool b[size];
LL n,ans,Gcd,step,next[size];
LL gcd(LL a,LL b);
void dfs(LL x);
int main() {
freopen("officer.in","r",stdin);
freopen("officer.out","w",stdout);
scanf("%lld",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&next[i]);
for(int i=1;i<=n;i++)
if(!b[i]) {
step=0; dfs(i);
if(ans==0) ans = step;
else {
Gcd = gcd(ans,step);
ans=(ans*step)/Gcd;
}
}
printf("%lld\n",ans);
return 0;
}
inline LL gcd(LL a,LL b) {
return b==0 ? a : gcd(b,a%b);
}
inline void dfs(LL x) {
b[x] = true;
step++;
if(b[next[x]]) return;
else dfs(next[x]);
}
2、拯救save.pas/c/cpp
【问题描述】
正义之士被恶魔抓了,被关在小黑屋里,无法继续他的正义事业,你决定去拯救他。
关正义之士的小黑屋迅速被你打开,可是正义之士却被恶魔用一把锁给锁住了。这把锁包含了N个小锁。只有打开前K-2个锁,且锁上第K-1个锁,才能改变第K个锁的状态(打开或锁上该锁),第1个锁可以任意改变状态,当第1个锁锁上时第2个锁就可以改变状态。
为了知道你到底是要留下来开锁,还是“走为上”,你需要知道到底需要多少次操作才能开锁(打开或锁上一把锁算一次操作,只有当N个小锁都被打开进才算开了锁)。
输入save.in
输入文件save.in第一行为一个N(小锁的个数, 1≤N≤1000 )。
第二行为n个整数a1,a2,…,an(每个都是0或者1),中间用单个空格隔开。 如果是ai=1,表示第i个锁是锁着的,反之表示该锁已被打开。
输出save.out
输出文件save.out包括一个数,表示最少要操作的次数。
【样例输入】
4
1 0 1 0
【样例输出】
6
【样例说明】
1010→1110→0110→0100→1100→1000→0000
【数据规模】
对于40%的数据,有 N≤30
对于100%的数据,有 N≤1000 ;
【题解】
这个题目我并没有严格的证明。思路如下
用数学归纳法我们可以知道将数串000…01(一共i个数,前i-1个数都是0,第i个数是1)最后变换成i个0所需要的次数是 2i−1−1
设 f[i]