2021 浙江省赛
按照我自认为的难度进行排序
A
签到题。
code
//
// Created by Lenovo on 2024/4/24.
//
#include<bits/stdc++.h>
#define int long long
using namespace std;
int b[10],r[10];
signed main(){
int sum1=0,sum2=0;
for(int i=1;i<=5;i++)cin>>b[i],sum1+=b[i];
for(int i=1;i<=5;i++)cin>>r[i],sum2+=r[i];
if(sum1>=sum2)cout<<"Blue\n";
else cout<<"Red\n";
}
M
诈骗概率,签到题。
题目描述
一共有 n 个人,其中的一个人 \(a\) 要和其他所有人 \(b_i\) 玩游戏。
对于一局游戏来讲,a 先选择一个 x,然后 \(b_i\) 再选择一个 y。
然后 a 给 b x点,b 给 a y 点。
如果 x 严格大于 y 那么 b 再给 a 10 点。
如果 y 严格大于 x 那么 a 再给 b 10 点。
询问完成 n - 1 次游戏之后的期望点数。
思路
因为每局游戏之间都是相互独立的,可以单独考虑一局游戏的期望值 \(s_i\)。
然后答案就是 \(ans = s_i * (n - 1)\) 。
对于一局游戏来讲,a 可以选的数字为 1-20,且考虑相等, b 可以选择的数字为 1-20,且概率相等,一共有 400 种不同的可能,经过手玩发现,他们的期望和一定为 0。
最后的答案就为 \(ans = 0 \times (n - 1) = 0\)。
code
//
// Created by Lenovo on 2024/4/24.
//
#include <bits/stdc++.h>
using namespace std;
int main() {
double n;
cin >> n;
double ans = 0;
for (int i = 1; i <= 20; i++) {
double num = 0;
for (int j = 1; j <= 20; j++) {
if (i < j) {
num += j - i - 10;
} else if (i > j) {
num += j - i + 10;
}
}
// num *= (20.0 - i) / 20 * (y - x - 10)
ans += num / 20;
}
ans /= 20;
ans *= (n - 1);
printf("0.0000");
}
C
题目概述
给你空间中的 8 个点,让你判断这 8 个点是否可以组成一个正方体。
思路
枚举八个点,然后对于每个点枚举剩余点,求出当前点和剩余点的距离的平方,
我们会得到 7 个距离,对于正方体来讲,一个点和剩余 7 个点的距离应该是 1 1 1 2 2 2 3 这样分配的,判断一下就好。
这样枚举八个点,可以确保是一个正常的正方体。
code
//
// Created by Lenovo on 2024/4/24.
//
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 103;
class Point {
public:
int x, y, z;
} p[10];
inline int dis(Point a, Point b) {
int x = a.x - b.x;
int y = a.y - b.y;
int z = a.z - b.z;
return x * x + y * y + z * z;
}
signed main() {
int t;
cin >> t;
while (t--) {
// cout << " t: " << t << "\n";
bool able = true;
for (int i = 1; i <= 8; i++) {
cin >> p[i].x >> p[i].y >> p[i].z;
}
// puts("LKP AK IOI");
for (int i = 1; i <= 8; i++) {
int cnt[4] = {0, 0, 0, 0};
int l[10] = {0};
int tot = 0, mi = 1e9;
for (int j = 1; j <= 8; j++) {
if (i == j) continue;
l[++tot] = dis(p[i], p[j]);
mi = min(mi, l[tot]);
}
int u = mi;
if (u == 0) able = false;
else for (int j = 1; j <= tot; j++) {
if (l[j] % u != 0) able = false;
cnt[l[j] / u]++;
}
if (cnt[1] != 3 || cnt[2] != 3 || cnt[3] != 1) able = false;
}
if (able) cout << "YES\n";
else cout << "NO\n";
}
return 0;
}
L
题目描述
一个唐人,写了一个假的字符串匹配算法。询问 S 中有几个 T。
因为这个算法是假的,问你什么时候 T 可以算成 假答案。
思路
观察这个小糖人的做法就可以看到,这个人没有将字符串中已经匹配的利用起来,也就是说一个字符至于 T 串中的一个位置匹配,也就是如果 T 中有和第一个位置的字符相同的,那就会算成假答案。
ex:
S:ababab T:abab S:abababc T:ababc
这两个都是错误答案。
code
//
// Created by Lenovo on 2024/4/24.
//
#include<bits/stdc++.h>
#define int long long
using namespace std;
int fail[100010];
char s[100010];
signed main(){
int m;
cin >> m;
for (int i = 1; i <= m; i++) {
cin >> s[i];
}
// fail[1] = 0;
for (int i = 2, j = 0; i <= m; i++) {
while (j && s[i] != s[j + 1]) j = fail[j];
if (s[i] == s[j + 1]) j++;
fail[i] = j;
}
// cout << fail[m] << "\n";
int flag = 0;
for (int i = 1; i <= m; i++)
if (fail[i]) flag = 1;
if (flag == 1) cout << "Wrong Answer\n";
else cout << "Correct\n";
}
J
题目描述
一个bfs加完全背包的题,队友开的。
然后还让我写了个完全背包,还写成了 01 背包。
然后价格和贡献还写反了。
code
//
// Created by Lenovo on 2024/4/24.
//
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 3e3 + 3;
const int MAXM = 1e4 + 3;
int first[MAXM], v[MAXM], nt[MAXM], a[MAXM];
int te;
inline void addEdge(int x, int y) {
nt[++te] = first[x];
first[x] = te;
v[te] = y;
}
struct Node{
int d, p;
};
int w[MAXN];
bool vis[MAXN];
inline void bfs() {
queue<Node> q;
q.push(Node{0, 1});
while (!q.empty()) {
int p = q.front().p;
int d = q.front().d;
q.pop();
if (vis[p]) continue;
w[p] = d * 2;
for (int eo = first[p]; eo; eo = nt[eo]) {
const int ep = v[eo];
q.push(Node{d + 1, ep});
}
vis[p] = true;
}
}
int n, m, t;
int f[MAXN];
signed main() {
cin >> n >> m >> t;
for (int i = 2; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= m; i++) {
int x, y;
cin >> x >> y;
addEdge(x, y);
addEdge(y, x);
}
bfs();
for (int i = 2; i <= n; i++) {
for (int j = w[i]; j <= t; j++) {
f[j] = max(f[j], f[j - w[i]] + a[i]);
}
}
for (int i = 1; i <= t; i++) {
cout << f[i] << ' ';
}
cout << '\n';
return 0;
}
I
题目描述
队友开题
经过队友拿着鼠标线和充电器线手玩之后成功的 A 了。
code
//
// Created by Lenovo on 2024/4/24.
//
#include<bits/stdc++.h>
using namespace std;
bool v[10];
int main(){
string s;
for(int i=1;i<=6;i++){
cin>>s;
if(s[0]=='t')v[i]=true;
else v[i]=false;
}
int cnt=0;
if(v[1]!=v[4])cnt++;
if(v[2]!=v[5])cnt++;
if(v[3]!=v[6])cnt++;
if(cnt==0){
if(v[2]==v[3]&&v[3]==v[5]&&v[5]==v[6])cout<<8<<endl;
else if(v[1]==v[2]&&v[2]==v[4]&&v[4]==v[5])cout<<8<<endl;
else cout<<7<<endl;
}
else if(cnt==1)cout<<6<<endl;
else if(cnt==2)cout<<5<<endl;
else cout<<4<<endl;
}
F
数论分块题。我被数学爆啥了呜呜。
题目描述
有 n 个机器人 m 个货物,可以进行两种操作,分别是减少机器人数量和增加货物数量。
问你要使 n 个机器人能够平均分到的货物数量相同,进行的最少得操作数是多少。
思路
假如,目前只剩下 \(s\) 个机器人,那么每个人说中的货物应该是 \(\left\lceil \frac{m}{s} \right\rceil\)
则当前情况下的最小操作次数就是 \(n - s + \left\lceil \frac{m}{s} \right\rceil \times s - m = (\left\lceil \frac{m}{s} \right\rceil - 1) \times s + (n - m)\)
因为 $ 1\leq s\leq n $ ,由数论分块可知,\(\left\lceil \frac{m}{s} \right\rceil\) 仅有 \(\sqrt n\) 种取值,然后考虑整除分块即可。
code
//
// Created by 86187 on 2024/4/25.
//
#include <bits/stdc++.h>
using namespace std;
int T, n, m;
int main() {
cin >> T;
while (T--) {
cin >> n >> m;
long long ans = 1e9;
for (long long j = n, i; j; j = i - 1) {
i = ceil(1.0 * m / ceil(1.0 * m / j));
long long k = ceil(1.0 * m / j) - 1;
ans = min(ans, k * i + n - m);
}
cout << ans << "\n";
}
}
G
题目描述
给出一个六边形状的蜂巢图。
有两种操作
1、占领一个六边形。这个人会在自己已经占领的地方造墙。
2、查询,给出的点相连接的领地中,墙的数量有多少。
思路
并查集,对于新加入的点来讲,找找该点附近有多少相邻的已经占领的领地,数量记为 cnt。那么最后算答案的时候减去 \(cnt \times 2\) 即可。
code
//
// Created by Lenovo on 2024/4/24.
//
#include <bits/stdc++.h>
#define N 500010
#define mp make_pair
#define int long long
using namespace std;
int n;
int fa[N], siz[N];
const int sx[6] = {1, 0, -1, -1, 0, 1};
const int sy[6] = {0, 1, 1, 0, -1, -1};
map<pair<int, int>, int> ma;
//map<pair<int, int>, int> ba;
int read() {
int s = 0, f = 0; char ch = getchar();
while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
return f ? -s : s;
}
int findFather(int x) {
if (fa[x] == x) return x;
return fa[x] = findFather(fa[x]);
}
signed main() {
n = read();
int cnt = 0;
// for (int i = 1; i <= n; i++) fa[i] = i;
for (int i = 1, opt, x, y; i <= n; i++) {
opt = read(), x = read(), y = read();
if (opt == 1) {
cnt++;
pair<int, int> s = mp(x, y);
ma[s] = cnt;
fa[cnt] = cnt;
siz[cnt] = 6;
int dxnum = 0;
for (int j = 0; j <= 5; j++) {
int ex = x + sx[j], ey = y + sy[j];
// cout << cnt << " " << ex << " " << ey << "\n";
pair<int, int> s1 = make_pair(ex, ey);
if (ma[s1]) {
// cout << cnt << " " << ex << " " << ey << "\n";
dxnum++;
int fs = findFather(ma[s]), fs1 = findFather(ma[s1]);
if (fs == fs1) continue;
fa[fs] = fs1;
siz[fs1] += siz[fs];
}
}
// cout << siz[findFather(ma[s])] << " " << dxnum << "\n";
siz[findFather(ma[s])] -= dxnum * 2;
// cout << siz[findFather(ma[s])] << "\n";
} else {
cout << siz[findFather(ma[mp(x, y)])] << "\n";
}
}
}
D
最短路建边题。
题目描述
给出 \(n\) 个点, \(m\) 条边的无向图,他的连边有一个特性,如果相连的两个点分别为 \(u, v\) ,那么 u 二进制一定是 v 的二进制下的前缀。然后有 \(q\) 个询问,每个询问给出两个点 \(s, t\) ,询问两点之间的最短路长度。
思路
乍一看毫无头绪,只能从特殊性质下手。
每个相连的两个点,一定是某个点的二进制等于某个点二进制的前缀的。不妨这样考虑,假如所有边中点与点的二进制只差一位,这个时候这张图会退化成一棵二叉树。然后我们发现,对于所有询问 \(s \rightarrow t\) 的询问,一定是先从 \(s\) 到达 \(s,t\) 的公共前缀,然后再到达 \(t\) 。假如说,将每个点 x,都与 \(x \times 2 + 1\) 和 \(x \times 2\) 连一条虚边,然后再把所有的给出的边加进去,这张图像极了一棵满二叉树,再加上由深度低的点连向深度高的点的边,这个建树的过程也像极了线段树建树的过程。
一个显然的想法是:对于每个询问,找到 s t 的公共前缀 u 作为中转点,在 u 这个点处的贡献就是 \(distance(u, s) + distance(u, t)\) ,考虑如何求每个点,到其前缀的点的距离。
在上述的建树过程中,dfs维护子树内的点集和子树根到其他点的距离,然后以当前的根节点跑 dijkstra 来预处理 distance 。根节点每次向下分叉的时候都会使点集的大小减半,所以均摊的预处理总时间是 \(O((n + m) log ^2 (n + m))\) 级别的。
预处理之后,在这棵树上暴跳求 \(s, t\) 的公共祖先 \(u\),然后求 \(min {distance(u, s) + distance(u, t)}\) 即可。
code
#include <bits/stdc++.h>
#define N 100010
#define M 200010
#define int long long
#define mp make_pair
using namespace std;
const int inf = 1e18 + 2;
int n, m, add_edge, q;
int head[N], dis[N], vis[N], tag[N];
map<pair<int, int>, int> road;
struct PPP {
int next, to, dis;
}edge[M << 1];
struct OOO {
int x, dis;
bool operator < (const OOO &b) const {
return dis > b.dis;
}
};
int read() {
int s = 0, f = 0; char ch = getchar();
while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
return f ? -s : s;
}
void add(int from, int to, int dis) {
edge[++add_edge].next = head[from];
edge[add_edge].dis = dis;
edge[add_edge].to = to;
head[from] = add_edge;
}
vector<int> init(int rt) {
vector<int> node;
if (rt > n) return node;
node.push_back(rt);
vector<int> lnode = init(rt << 1);
vector<int> rnode = init(rt << 1 | 1);
priority_queue<OOO> q;
for (auto x : lnode) node.push_back(x);
for (auto x : rnode) node.push_back(x);
for (auto x : node) {
dis[x] = inf;
vis[x] = 0;
}
dis[rt] = 0;
q.push((OOO){rt, 0});
while (!q.empty()) {
int x = q.top().x; q.pop();
if (vis[x]) continue;
vis[x] = 1;
for (int i = head[x]; i; i = edge[i].next) {
int to = edge[i].to;
if (dis[x] + edge[i].dis < dis[to]) {
dis[to] = dis[x] + edge[i].dis;
q.push((OOO){to, dis[to]});
}
}
}
for (auto x : node) {
road[mp(rt, x)] = dis[x];
}
return node;
}
int query(int x, int y, int ta) {
int ans = inf;
for (int i = x; i; i >>= 1) tag[i] = ta;
for (int i = y; i; i >>= 1)
if (tag[i] == ta) {
ans = min(ans, road[mp(i, x)] + road[mp(i, y)]);
}
if (ans == inf) return -1;
else return ans;
}
signed main() {
n = read(), m = read();
for (int i = 1, x, y, dis; i <= m; i++) {
x = read(), y = read(), dis = read();
add(x, y, dis);
add(y, x, dis);
}
init(1);
q = read();
for (int i = 1, x, y; i <= q; i++) {
x = read(), y = read();
cout << query(x, y, i) << "\n";
}
}

浙公网安备 33010602011771号