文章目录
- [A ★★比赛新机制★★](https://ac.nowcoder.com/acm/contest/16520/A)
- [C ★★生命的游戏★★](https://ac.nowcoder.com/acm/contest/16520/C)
- [E ★★序列大团结★★](https://ac.nowcoder.com/acm/contest/16520/E)
- [F ★★飞马分隔符★★](https://ac.nowcoder.com/acm/contest/16520/F)
- [H ★★温暖的力量★★](https://ac.nowcoder.com/acm/contest/16520/H)
- [I ★★平形四边行★★](https://ac.nowcoder.com/acm/contest/16520/I)
- [J ★★翻滚吧硬币★★](https://ac.nowcoder.com/acm/contest/16520/J)
A ★★比赛新机制★★
最近,AllenAllen 教授研发了一种新的比赛机制,这种比赛机制是 ACPC (AsiaCollegiateProgrammingContest) 赛制的扩展,简称 AUPC (AsiaUniversityProgrammingContest)。参赛者为个人参赛(即每人一台电脑),且成绩分为两部分,过题数和罚时。每道题目的罚时 s 与比赛已经开始的时间(按分钟计) a 以及该题错误的提交次数(不包括编译错误) b 有关,即 s=a+b×20。
另外,新赛制规定,每位参赛者必须按一定的顺序循环做题,直到解出全部题目或比赛结束。在比赛开始之前,每位参赛者可以任意选择第一个要做的题目和方向:正序或者逆序,但是中途不能跳题或者改变方向。例如,现在有 A,B,C,D,E 五道题目,参赛者可以选择第一个要做的题目 C,然后按正序 C,D,E,A,B 的顺序做题,或者按逆序 C,B,A,E,D 的顺序做题,而 C,D,B,A,E 或 C,E,A,B,D 都是不被允许的。Alice 水平非常高,她总是可以做出所有的题目。现在她想知道,如果已知解决每道题需要花费的时间,那么解决 n 道题的最少罚时是多少。我们认为比赛时间足够 Alice 解决所有题目,在比赛开始的瞬间 Alice 就开始做题并且所有题目都是一次提交便成功通过。
反思
在列公式找规律的时候,可能是太困了(不找理由),相减式子还少减一个,减法不会了呜呜
题意
就是从某个前开始,往一定的顺序,带上系数,求得最小的这个和就是答案
思路
我们观察发现,我们全部加上一个 a 1 + a 2 + a 3 + a 4 a_1+a_2+a_3+a_4 a1+a2+a3+a4就能变成下个可能的方案了,而 a 1 a_1 a1却多了4个,那我们把它减掉
那在下一种方案,我们就能发现也是减4个 a 2 a_2 a2,慢慢算下去,这就是一个规律
注意这题卡cin
Code
// Problem: ★★比赛新机制★★
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/16520/A
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// Code by: ING__
//
// Powered by CP Editor (https://cpeditor.org)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
#define Endl "\n"
#define endl "\n"
using namespace std;
typedef pair<int, int> PII;
int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};
int T;
int n;
int a[500000 + 10];
int main(){
cin >> T;
while(T--){
ll s1 = 0;
ll s2 = 0;
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d", a + i);
s1 += ((ll)a[i] * (n - i + 1)); // 会爆int!!
s2 += a[i];
}
ll ans = s1;
for(int i = 2; i <= n; i++){
s1 += s2;
s1 -= (n * (ll)a[i - 1]);
ans = min(ans, s1);
}
reverse(a + 1, a + 1 + n);
s1 = 0;
s2 = 0;
for(int i = 1; i <= n; i++){
s1 += ((ll)a[i] * (n - i + 1));
s2 += a[i];
}
ans = min(ans, s1);
for(int i = 2; i <= n; i++){
s1 += s2;
s1 -= (n * (ll)a[i - 1]);
ans = min(ans, s1);
}
cout << ans << endl;
}
re 0;
}
C ★★生命的游戏★★
生命游戏(Conway′s Game of Life)是英国数学家约翰·何顿·康威在 1970 年发明的一款“零玩家游戏”。
生命游戏的“棋盘”是一个 n×n 的网格,每个网格代表一个“细胞”,每个细胞有两种状态:“生”或“死”。生命游戏每隔一段时间进行一次演变,称为一个“回合”。
在生命游戏中,每个细胞在一个新回合的生与死只取决于它在上一个回合时周围 8 个细胞的存活状态,具体的规则如下:
- 如果一个死亡的的细胞周围恰好有 3 个活的细胞,那么下一个回合时,这个细胞的状态将转为“生”
- 如果一个存活的的细胞周围活细胞的数量大于 3 或小于 2,那么下一个回合时,这个细胞的状态将转为“死”
- 其他情况下,细胞的存活状态不变
特别地,我们规定第 i 行第 j 列的细胞为 (i,j),且在“棋盘”边缘的细胞在位置上与另一侧循环相连。例如细胞 (n,n) 的右侧是细胞 (n,1),下方是细胞 (1,n)。
Cindy 特别喜欢生命游戏,她常常能够在看似毫无规律的生命游戏中发现许多有趣的现象。例如对于某些初始状态,会在若干回合的演变之后再次回到最初的状态,即所有对应位置的细胞状态完全相同,我们便认为该初始状态存在周期。
例如,图 C−1 在进行一次演变之后会变成图 C−2,再进行一次演变后又会重新变为 C−1,其周期为 2。(其中 ★ 代表“生”)
对于一个给定的初始状态,Cindy 想知道在 k 回合之内(包括第 k 回合)该状态是否存在周期。
反思
看到慌了;但是一看才知道数据范围不大,直接模拟就可以
题意
根据“ * ” 的要求来就行,
题解
直接模拟,注意细节
Code
// Problem: ★★生命的游戏★★
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/16520/C
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// Code by: ING__
//
// Powered by CP Editor (https://cpeditor.org)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
#define Endl "\n"
#define endl "\n"
using namespace std;
typedef pair<int, int> PII;
const int N = 110;
int dx[8] = {-1,0,1,0,1,1,-1,-1};
int dy[8] = {0,1,0,-1,1,-1,1,-1};
int T;
int n ,k;
int a[N][N]; // 初始
int b[N][N]; // 上一次
int c[N][N]; // 从上一次变过来的这一次
int num[N][N]; // 某个点周围有多少个1
int main(){
cin >> T;
while(T--){
cin >> n >> k;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
scanf("%d", a[i] + j);
b[i][j] = a[i][j];
}
}
int m = k;
int t = 0;
while(k--){
memset(num, 0, sizeof(num)); // 细节
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
for(int l = 0; l < 8; l++){
int x = i + dx[l];
int y = j + dy[l];
if(x == n + 1){
x = 1;
}
else if(x == 0){
x = n;
}
if(y == n + 1){
y = 1;
}
else if(y == 0){
y = n;
}
if(b[x][y])
num[i][j]++;
}
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(!b[i][j] && num[i][j] == 3)
c[i][j] = 1;
else if(b[i][j] && (num[i][j] > 3 || num[i][j] < 2))
c[i][j] = 0;
else
c[i][j] = b[i][j];
}
}
int f = 1;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(a[i][j] != c[i][j]){
f = 0;
break;
}
}
}
if(f){
t = 1;
break;
}
memcpy(b, c, sizeof(c));
}
if(t){
cout << "YES" << endl;
cout << m - k << endl;
}
else{
cout << "NO" << Endl;
}
}
re 0;
}
E ★★序列大团结★★
Ellis 热爱集体,向往团结。她喜欢一切团结的事物,例如团结的班级,团结的家庭,甚至是团结的序列。
EllisEllisEllis 认为,如果对于所有 1≤x≤max{Ei},都恰好满足以下条件之一:
x 没有在序列 E 中出现过
x 是 k 的倍数
x 在序列 E 中的出现次数是 k 的倍数
那么就称序列 E 是团结的,其“团结因子”为 k。
同时,Ellis 认为,对于一个“团结因子”为 k 的序列,其“团结度”为“团结因子”同样为 k 的不同非空连续子序列的数量。
一个序列的连续子序列为原序列中下标连续的任意子序列,即在原序列中截取了一段。例如 [2,3,4][1,2,3,4,5] 的连续子序列,而 [1,3,5] 则不是。不同的连续子序列指的是从原序列中截取的位置不同,即使不同子序列中所包含数的大小和顺序可能完全相同。
Ellis 想知道,对于一个给定的序列 E 和“团结因子” k,它的“团结度”是多少。
哈希,代补,没看懂题
F ★★飞马分隔符★★
Ford 非常喜欢FeiMa,如果一个字符串中存在一个子序列为FeiMa,Ford 就会异常兴奋,然而这可能会导致一些无法预料的后果。
子序列为原字符串中删去若干字符,剩余字符相对位置不变形成的序列。例如ac,abcd均是abcd的子序列,而ca则不是abcd的子序列。
为了防止意外发生,你需要将这个字符串 s 按顺序划分为若干部分 s1,s2,…,sk,且 s1+s2+⋯+sk=s(+ 表示字符串的连接),使得每一部分都不包含子序列FeiMa。而每次划分都需要一个“分隔符”,每个“分隔符”都要去商店购买。
现在,你想知道最少需要购买多少个分隔符。
反思
读题读题读题!
没看到子序列直接当子串来做,还没看到总共1e5,以为是1e5 * 1e5那肯定必须得线性做,然后搞了半天的哈希比较
题意
本质问你有几个"FeiMa"字符串序列在s中,有多少个就能把他分开多少次
思路
直接找子序列看看有多少个
Code
// Problem: ★★飞马分隔符★★
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/16520/F
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// Code by: ING__
//
// Powered by CP Editor (https://cpeditor.org)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
#define Endl "\n"
#define endl "\n"
using namespace std;
int T;
int n;
char s[100000 + 10];
bool st[8];
int main(){
cin >> T;
while(T--){
cin >> n;
// memset(s, 0, sizeof(s));
memset(st, 0, sizeof(st));
scanf("%s", s + 1);
int ans = 0;
for(int i = 1; i <= n; i++){
if(s[i] == 'F') st[1] = 1;
if(s[i] == 'e' && st[1]) st[2] = 1;
if(s[i] == 'i' && st[2]) st[3] = 1;
if(s[i] == 'M' && st[3]) st[4] = 1;
if(s[i] == 'a' && st[4]){
memset(st, 0, sizeof(st));
ans++;
}
}
cout << ans << endl;
}
re 0;
}
H ★★温暖的力量★★
小时候,Helen 曾感受到一份与众不同的温暖。那份温暖,蕴含着令人重生的力量。即便过去了很久很久,她依然无法忘却。
随着时间的流逝,这份温暖被随机分成了 x (x>1) 份。由于温暖之间的羁绊,每份温暖分到的力量都是质数,并且力量总和与原来相同。
为了重新寻回这份温暖,必须知道 x 最大可能为多少。但是 Helen 思考了很久,也未能如愿。
作为 Helen 的好友,你不想再看到她为此忧伤。因此你需要帮助 Helen 找到这个最大的 x 值。
反思
加法跟质因数分解有啥关系,直接都取2,除不尽最后一个1给一个2凑成3也是质数
题意
挺好理解的,不叙述了
题解
在反思里
Code
// Problem: ★★温暖的力量★★
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/16520/H
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// Code by: ING__
//
// Powered by CP Editor (https://cpeditor.org)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int T;
int s;
int main(){
cin >> T;
while(T--){
cin >> s;
if(s <= 3) cout << -1 << endl;
else cout << s / 2 << endl;
}
}
I ★★平形四边行★★
Isabella 非常喜欢四边形,尤其是较为特殊的平行四边形。
众所周知,两组对边分别平行且相等的四边形是平行四边形。Isabella 可以很轻松地从平面中找到 44 个不同的点,使它们能够围成一个平行四边形,但是她并不满足于此。如果是给定的若干个点,再想从中找到满足条件的 4 个点,则会变得十分棘手。Isabel 是一个不喜欢麻烦的人,她定义了一种新规则,两组对边分别相等的图形称为“平形四边行”,即使四个顶点可能共线甚至重合。这看起来似乎少了一些限制,但 Isabel 依然相信这并不是一个简单的问题。
Isabel 想知道,在平面内给定的若干个点中,是否存在 4 个不同的点能够构成“平形四边行”。(即使是两个坐标完全相同的点,只要它们的下标不同,Isabella 就认为这两个点是不同的。例如,即使 x i = x j , y i = y j x_{i}=x_{j},y_{i}=y_{j} xi=xj,yi=yj,但只要 i ≠ j i \neq j i=j,那么这两个点就被认为是不同的)
题解
官方:四个点能形成“平形四边行”的充要条件是,存在一种方案,将四个点均分为两组,每组的两个点形成一条 线段,这两条线段的中点重合。
注意到桶的大小只有 ,即第 个点落下时,一定存在重合的中点,从而构成 一组解。另外,桶不可能被完全填满,因此复杂度远远小于预期。
于是 暴力枚举每组点,存储所形成线段的中点,直到存在重合的中点,同时应注意过滤掉存在交集的 两组点。 时间复杂度 。
Code
// Problem: ★★平形四边行★★
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/16520/I
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// Code by: ING__
//
// Powered by CP Editor (https://cpeditor.org)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
#define Endl "\n"
#define endl "\n"
using namespace std;
typedef pair<int, int> PII;
const int N = 4e5 + 10;
int n;
PII v[4010][4010];
int x[N];
int y[N];
int main(){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> x[i] >> y[i];
for(int j = 1; j < i; j++){
int dx = x[i] + x[j] + 2000; // 因为有负数,我们可以把负数整体偏移1000,又因为求中点,然后再乘2
int dy = y[i] + y[j] + 2000;
if (i == v[dx][dy].first || j == v[dx][dy].first)
continue;
if (i == v[dx][dy].second || j == v[dx][dy].second)
continue;
if(v[dx][dy].first){
cout << "YES" << Endl;
cout << i << " " << j << " " << v[dx][dy].first << " " << v[dx][dy].second;
return 0;
}
v[dx][dy] = {i , j};
}
}
cout << "NO";
return 0;
}
J ★★翻滚吧硬币★★
Jack 是一个喜欢动脑的孩子,在他的生日这天,他收到了一份神奇的礼物:三枚硬币以及一封信。
信中描述了这样一个情景:任取两枚硬币固定在二维平面上,并让它们恰好相切,用第三枚硬币沿着它们形成的边界进行翻滚,即时刻保证与至少一枚已固定的硬币相切。这样这枚运动的硬币在翻滚了一定的圈数之后,一定会回到原点,即恰好绕了一周。(如此一来,便会出现三种情况:固定 A,B,让 C 运动;固定 A,C,让 B 运动;固定 B,C,让 A 运动)
信里还说,如果 Jack 能求出运动的硬币翻滚的圈数最少是多少,那么他将会收获一份更大的礼物。Jack 非常想要这份大礼,但是又不知道如何解决这个难题,因此他向你求助问题的答案。
题意
怎么转圈,让路径最少
题解
官方说的很详细了,就是涉及到了很多数学函数的运用
Code
// Problem: ★★翻滚吧硬币★★
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/16520/J
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// Code by: ING__
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
#define ll long long
#define re return
#define Endl "\n"
#define endl "\n"
using namespace std;
typedef pair<int, int> PII;
const long double pi = acosl(-1);
int T;
int r[10];
void work(){
long double ans, a, b, c, x, y;
cin >> r[1] >> r[2] >> r[3];
sort(r + 1, r + 3 + 1);
a = r[1] + r[2], b = r[1] + r[3], c = r[2] + r[3];
x = acosl((a * a + b * b - c * c) / (2.0l * a * b));
y = acosl((a * a + c * c - b * b) / (2.0l * a * c));
ans = ((pi - x) * b + (pi - y) * c) / (pi * r[3]);
cout << fixed << setprecision(15) << ans << endl;
}
int main(){
cin >> T;
while(T--){
work();
}
}