这次 vp 我队出了 5 题,应当能拿铜,但是还不够,银牌也应当争取。 总之一个原则,保铜争银吧
A - 2020
水题不解释。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n; string s;
while(cin >> n)
{
cin >> s;
s = " " + s;
int cnt = 0;
for(int i = 1; i <= n - 3; ++i)
{
string tmp = s.substr(i, 4);
if(tmp == "2020"){
++cnt;
i = i + 3;
}
}
cout << cnt << '\n';
}
return 0;
}
B - 2020 vs 2018
由于 “2020” 和 “2018” 两者比较显著好判断的区别在于 “2018” 有个 “1”,因此只需判断字符画中是否能找到 “1” 即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 55;
char g[N][N];
int n, m;
void solve()
{
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= m; ++j)
{
cin >> g[i][j];
}
}
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= m; ++j)
{
if(g[i][j] == 'o')
{
if(g[i][j + 1] != 'o' && g[i][j - 1] != 'o')
{
if(g[i - 1][j] != 'o')
{
puts("2018");
return ;
}
}
}
}
}
puts("2020");
}
int main()
{
while(cin >> n >> m)
{
solve();
}
return 0;
}
D - String Commutativity
做法:
- 利用字符串哈希求出每个字符串的最小循环节,并将所有循环节的哈希映射值丢到一个哈希桶中(unordered_map),最后按照 “小朋友握手” 原则统计输出即可求解。
为什么要求循环节?
- 因为只有当一对字符串 s1、s2 的最小循环节相同时,才会满足 s1 + s2 = s2 + s1。因此我们将所有字符串的循环节放到哈希桶中计数,就能求出所有满足题意的字符串对数。
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define map unordered_map
typedef unsigned long long ull;
const int N = 1e6 + 10, M = N << 1, P = 1331;
int n;
ull h[M], p[M];
ull get(int l, int r)
{
return h[r] - h[l - 1] * p[r - l + 1];
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
while (cin >> n)
{
int t = n;
map<ull, int> hah;
while (t--)
{
string s; cin >> s; s = " " + s + s;
p[0] = 1;
for (int i = 1; i <= s.size() - 1; ++i)
{
h[i] = h[i - 1] * P + s[i];
p[i] = p[i - 1] * P;
}
int len = (s.size() - 1) / 2;
for (int i = 2; i <= len + 1; ++i)
{
int a = i, b = i + len - 1;
if (get(1, len) == get(a, b))
{
hah[get(1, a - 1)]++;
break;
}
}
}
int res = 0;
for (auto vv : hah)
{
res += vv.second * (vv.second - 1) / 2;
}
printf("%lld\n", res);
}
return 0;
}
G - 奇矩阵
根据数据范围,暴力即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 1010, M = 1010;
int n, m;
int a[N][N];
inline void solve()
{
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= m; ++j)
{
scanf("%d", &a[i][j]);
}
}
for(int i = 1; i <= n - 1; ++i)
{
for(int j = i + 1; j <= n; ++j)
{
int sum = 0;
for(int k = 1; k <= m; ++k)
{
sum += abs(a[i][k] - a[j][k]);
}
if((sum & 1) == 0){
puts("No");
return ;
}
}
}
puts("Yes");
return ;
}
signed main()
{
while(~scanf("%d%d", &n, &m))
{
solve();
}
return 0;
}
I - 共线点
这是一道计算几何题,首先谈谈一般计算几何有什么坑点
- 由于这类题目用到的变量一般是 double 型,因此我们要避免对变量进行过多运算,能用一个式子算出答案的就不要用多个式子,这样可以有效避免精度丢失而 wa 的问题。
- 另外,直线方程的选择也是有讲究的,如果已知两点那就用两点式,这样也可以减少运算避免精度问题。
思路:
将第一根线段左端点和第二根线段右端点联立出第一条直线方程,将第一根线段右端点和第二根线段左端点联立出第二条直线方程。
另第三条线段所在直线与上面求出来的两条直线求交点,两个交点形成的线段与第三根线段如果有交集,则表示有一条直线能够同事穿过三根线段,反之没有。
写代码的时候要尽可能用一个式子算答案,简化代码。
#include <bits/stdc++.h>
using namespace std;
typedef long double db;
db a1, b1, Y1, a2, b2, y2, a3, b3, y3;
bool check(db a, db b, db c, db d)
{
if (a > c) swap(a, c), swap(b, d);
return c <= b;
}
inline void solve()
{
db xx = (y3 - y2) / (Y1 - y2) * (b1 - a2) + a2;
db yy = (y3 - y2) / (Y1 - y2) * (a1 - b2) + b2;
if (check(xx, yy, a3, b3)) puts("Yes");
else puts("No");
}
signed main()
{
while (cin >> a1 >> b1 >> Y1 >> a2 >> b2 >> y2 >> a3 >> b3 >> y3)
{
solve();
}
return 0;
}