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";
  }
}
posted @ 2024-04-25 22:57  Kersen  阅读(70)  评论(2)    收藏  举报