NEFU 10.9(SWERC2020)

A.Gratitude

  • 题意

给定 3 n 3n 3n个字符串和参数 K K K,输出这些字符串中出现次数最多的前 K K K个;出现次数相同的字符串,输出最后一次出现最晚的一个。
1 ≤ K ≤ 3 N ≤ 100 , 000 1\le K\le 3N\le 100,000 1K3N100,000

  • 思路
    我自己是用了map反复横跳存储信息,但是我的队友赛后又给出了一套比我简洁n倍的代码
  • 代码
/*
 * @Author: Icey_dying
 * @Date: 2021-10-09 16:26:00
 * @LastEditors: Icey_dying
 * @LastEditTime: 2021-10-09 16:47:28
 * @FilePath: \Icey_dying\competition\2021\2021.10\2021.10.9\A.cpp
 */
#include <bits/stdc++.h>
using namespace std;
struct sa {
    int id;
    int num;
};
bool cmp(const sa& a, const sa& b)
{
    if (a.num != b.num)
        return a.num > b.num;
    return a.id > b.id;
}
sa a[100005];
int n, k, cnt1;
string s;
set<string> se;
map<string, int> m1;
map<int, string> m2;
int cnt[100005];
int main()
{
    memset(cnt, 0, sizeof(cnt));
    cin >> n >> k;
    getchar();
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= 3; j++) {
            getline(cin, s);
            if (se.find(s) == se.end()) {
                se.insert(s);
                m1[s] = ++cnt1;
                m2[cnt1] = s;
                cnt[cnt1]++;
            } else {
                cnt[++cnt1] = cnt[m1[s]] + 1;
                cnt[m1[s]] = 0;
                m1[s] = cnt1;
                m2[cnt1] = s;
            }
        }
    }
    for (int i = 1; i <= cnt1; i++)
        a[i] = { i, cnt[i] };
    sort(a + 1, a + 1 + cnt1, cmp);
    // for (int i = 1; i <= cnt1; i++)
    //     cout << a[i].id << ' ' << a[i].num << "\n";
    for (int i = 1; i <= k && i <= cnt1; i++)
        if (a[i].num)
            cout << m2[a[i].id] << "\n";
    return 0;
}

在此附上dalao的代码

/*
 * @Author: NEFU AB-IN
 * @Date: 2021-10-09 16:20:21
 * @FilePath: \ACM\CF\2021.10.9\a.cpp
 * @LastEditTime: 2021-10-09 16:52:17
 */
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define MP make_pair
#define SZ(X) ((int)(X).size())
#define IOS                                                                                                            \
    ios::sync_with_stdio(false);                                                                                       \
    cin.tie(0);                                                                                                        \
    cout.tie(0);
#define DEBUG(X) cout << #X << ": " << X << endl;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;

unordered_map<string, int> xianhou;
unordered_map<string, int> cnt;

struct sa
{
    string s;
    int xianhou, cnt;

    bool operator<(const sa &a) const
    {
        if (cnt != a.cnt)
            return cnt > a.cnt;
        else
            return xianhou > a.xianhou;
    }
    sa(string s, int xianhou, int cnt) : s(s), xianhou(xianhou), cnt(cnt){};
};

signed main()
{
    IOS;
    int n, k;
    string s;
    cin >> n >> k;
    getline(cin, s);
    for (int i = 1; i <= 3 * n; ++i){
        getline(cin, s);
        cnt[s]++;
        xianhou[s] = i;
    }
    vector<sa> v;
    for (auto [a, b] : xianhou)
        v.emplace_back(sa(a, b, cnt[a]));
    sort(v.begin(), v.end());

    for (int i = 0; i < min(k, SZ(v)); ++i)
        cout << v[i].s << '\n';
    return 0;
}

C.Safe Distance

  • 题意

给定 x O y xOy xOy平面和平面上的若干个点,要从 ( 0 , 0 ) (0,0) (0,0)出发到达 ( X , Y ) (X,Y) (X,Y),要求路径距离给出的点尽可能远,求出路径与最近的点的距离的最大值
1 ≤ X , Y ≤ 1 0 6 1\le X,Y\le 10^6 1X,Y106
1 ≤ n ≤ 1000 1\le n \le 1000 1n1000

  • 思路
    考虑二分答案
    假设我们二分得到的答案为 m i d mid mid,对其检查可行性
    在平面上以所有的点为圆心画半径为 m i d mid mid的圆,那么路径不应该与这些圆有交点
    想象将这些圆在平面上挖去,那么如果 m i d mid mid为可行答案, ( 0 , 0 ) (0,0) (0,0) ( X , Y ) (X,Y) (X,Y)应仍连通
    接下来考虑如何表示 ( 0 , 0 ) (0,0) (0,0) ( X , Y ) (X,Y) (X,Y)的连通性
    假设我们要将平面沿一条线(可以是曲线)切开,使得 ( 0 , 0 ) (0,0) (0,0) ( X , Y ) (X,Y) (X,Y)不连通,这条线应当与平面的左边界和下边界、或左边界和右边界、或上边界和右边界、或上边界和下边界同时存在交点
    那么如果我们画的圆的并能够包含这样的一条线, ( 0 , 0 ) (0,0) (0,0) ( X , Y ) (X,Y) (X,Y)就是不连通的
    我们可以以所有的点为顶点(称为平面点),点之间的距离为边权建立无向图,同时设置四个顶点 u t , u b , u l , u r u_t,u_b,u_l,u_r ut,ub,ul,ur(称为边界点)分别代表上、下、左、右边界,平面点与边界点间的边的边权为点到对应边界的距离
    那么,如果在图上存在一条路径分别以 u l , u b u_l,u_b ul,ub、或 u l , u r u_l,u_r ul,ur、或 u t , u r u_t,u_r ut,ur、或 u t , u b u_t,u_b ut,ub为起点和终点,满足边界点到平面点的边的边权小于等于 m i d mid mid,平面点之间的边的边权小于等于 2 ∗ m i d 2*mid 2mid,那么 ( 0 , 0 ) (0,0) (0,0) ( X , Y ) (X,Y) (X,Y)就是不连通的
    复杂度 O ( n log ⁡ ( X + Y ) ) O(n\log (X+Y)) O(nlog(X+Y))
  • 代码
/*
 * @Author: Icey_dying
 * @Date: 2021-10-11 11:05:09
 * @LastEditors: Icey_dying
 * @LastEditTime: 2021-10-11 11:32:28
 * @FilePath: \Icey_dying\competition\2021\2021.10\2021.10.9\C.cpp
 */
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN = 1e3 + 9, MOD = 1e9 + 7;
ll n, m;

double a[MAXN], b[MAXN];
int f[MAXN];
int find(int x)
{
    return f[x] == x ? x : f[x] = find(f[x]);
}
bool check(double mid)
{
    for (int i = 1; i <= n + 4; i++)
        f[i] = i;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n + 4; j++) {
            if (i == j)
                continue;
            if (j == n + 1 || j == n + 2)
                a[j] = a[i];
            if (j == n + 3 || j == n + 4)
                b[j] = b[i];
            double dis = sqrt(pow(a[j] - a[i], 2) + pow(b[j] - b[i], 2));
            if (j <= n && dis < 2 * mid || j > n && dis < mid) {
                int pa = find(i), pb = find(j);
                f[pa] = pb;
            }
        }
    }
    if (find(n + 1) == find(n + 2))
        return 0;
    if (find(n + 1) == find(n + 3))
        return 0;
    if (find(n + 3) == find(n + 4))
        return 0;
    if (find(n + 2) == find(n + 4))
        return 0;
    return 1;
}
int x, y;
void solve()
{
    cin >> x >> y >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i] >> b[i];
    b[n + 1] = a[n + 3] = 0;
    b[n + 2] = y;
    a[n + 4] = x;
    double l = 0, r = 1e6;
    for (int i = 1; i <= 40; i++) {
        double mid = (l + r) / 2;
        if (check(mid))
            l = mid;
        else
            r = mid;
    }
    cout << l << endl;
}
int main()
{
    ios::sync_with_stdio(0), cin.tie(0);
    solve();
    return 0;
}

D.Jogging

  • 题意

定义一个跑步路径为一条从0号顶点出发再回到0号顶点的路径,跑步路径上允许出现重边,允许在一条边上的任意位置折返。如果一条跑步路径经过了之前未出现过的边,那么这条跑步路径就是“有趣的”。
给定 I I I个点 S S S条边的简单无向带权图,下界 L L L和上界 U U U。现要求每天都要有一条”有趣的"跑步路径,且长度在上界和下界之间,问最多可以跑步多少天。
1 ≤ I , S ≤ 100000 1\le I,S\le 100000 1I,S100000
1 ≤ L ≤ U ≤ 42195 1\le L\le U\le 42195 1LU42195(一次马拉松的长度)
1 ≤ 边 权 ≤ 1000 1\le 边权\le 1000 11000

  • 思路
    由于我们可以在一条边上来回摩擦,所以不需要关心下界
    如果每条跑步路径不原路返回,会使答案更劣,所以原路返回一定是最佳策略
    那么题目可以转化成从顶点 0 0 0出发,行走不大于 U 2 \frac{U}{2} 2U的距离,最多可以到达多少条边
  • 代码
/*
 * @Author: Icey_dying
 * @Date: 2021-10-11 12:10:27
 * @LastEditors: Icey_dying
 * @LastEditTime: 2021-10-11 12:21:24
 * @FilePath: \Icey_dying\competition\2021\2021.10\2021.10.9\D.cpp
 */
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pa pair<int, int>
const int N = 1e5 + 5;
int n, m, L, U;
struct edge {
    int u, v, w, next;
} g[N * 2];
int head[N], cnt;
void add(int u, int v, int w)
{
    g[++cnt].u = u;
    g[cnt].v = v;
    g[cnt].w = w;
    g[cnt].next = head[u];
    head[u] = cnt;
}
int dist[N], vis[N];
void djs()
{
    memset(dist, 0x3f, sizeof(dist));
    dist[0] = 0;
    priority_queue<pa> q;
    q.push({ -dist[0], 0 });
    while (!q.empty()) {
        int u = q.top().second;
        q.pop();
        if (vis[u])
            continue;
        vis[u] = 1;
        for (int i = head[u]; i; i = g[i].next) {
            int v = g[i].v;
            if (!vis[v] && dist[u] + g[i].w < dist[v]) {
                dist[v] = dist[u] + g[i].w;
                q.push({ -dist[v], v });
            }
        }
    }
}
int main()
{
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> n >> m >> L >> U;
    for (int i = 1, u, v, w; i <= m; i++) {
        cin >> u >> v >> w;
        add(u, v, w);
        add(v, u, w);
    }
    djs();
    int ans = 0;
    for (int i = 1; i <= cnt; i += 2)
        if (min(dist[g[i].u], dist[g[i].v]) * 2 < U)
            ans++;
    cout << ans << "\n";
    return 0;
}

E.Cakes

  • 题意

n种原料,给定制作一个蛋糕所需要的各种原料的量 a i a_i ai和现有的量 b i b_i bi,求最多能做多少个蛋糕
n ≤ 10 n\le 10 n10

  • 思路
    输出 m i n { b i a i } min\{\frac{b_i}{a_i}\} min{aibi}
  • 代码
/*
 * @Author: NEFU AB-IN
 * @Date: 2021-10-09 15:44:18
 * @FilePath: \ACM\CF\2021.10.9\e.cpp
 * @LastEditTime: 2021-10-09 15:46:10
 */
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define MP make_pair
#define SZ(X) ((int)(X).size())
#define IOS                                                                                                            \
    ios::sync_with_stdio(false);                                                                                       \
    cin.tie(0);                                                                                                        \
    cout.tie(0);
#define DEBUG(X) cout << #X << ": " << X << endl;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;

signed main()
{
    IOS;
    int n;
    cin >> n;
    int mn = INF;
    for (int i = 1; i <= n; ++i)
    {
        int a, b;
        cin >> a >> b;
        mn = min(b / a, mn);
    }
    cout << mn << '\n';
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值