因为是久远的比赛,没有什么特别难的题。

没有 DP 好评()

A

Description

有 $n$ 个大人 $m$ 个小孩,一枚车票一元钱,一个大人可以带很多小孩,一个大人能免费带一个小孩上车,每个小孩都必须有一个大人带。问最少/最多需要花多少钱。

Solution

又水又毒瘤,不写了,看代码。

Code

#include <iostream>
#include <algorithm>
using std::cin;
using std::endl;
using std::cout;
 
int main()
{
    int a, b;
    cin >> a >> b;
    if (a == 0 && b != 0) // 有小孩没大人
        puts("Impossible");
    else if (b == 0) // 有大人没小孩
        cout << a << " " << a << endl;
    else
        cout << std::max(a, b) << " " << a + b - 1 << endl; 
    // 最少就是尽量一个大人带一个小孩,最大就是一个大人带所有小孩
}

B

Description

平面上有两个圆,输出一个能连接这两个圆的圆的最小半径。

Solution

分两个圆相离、内含、相交(相切)算一下就行,具体看代码。

Code

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
using std::cin;
using std::endl;
using std::cout;

inline double pow2(double x) { return x * x; }

inline void updmi(double &a, double b) { a = std::min(a, fabs(b) / 2.0); }
inline void out(double x) { printf("%.15lf\n", x); }

int main()
{
    double x1, y1, r1, x2, y2, r2;
    cin >> x1 >> y1 >> r1 >> x2 >> y2 >> r2;
    double dis = sqrt(pow2(x1 - x2) + pow2(y1 - y2));
    if (r1 < r2)
        std::swap(r1, r2);
    if (dis > r1 + r2) // 相离,即样例的那两个图
        out((dis - r1 - r2) / 2.0);
    else if (r2 + dis < r1) // 内含,圆的直径是大半径减小半径减两个圆圆心的距离
        out((r1 - r2 - dis) / 2.0);
    else // 相交或相离,显然答案是 0
        out(0.0);
}

C

Description

给出若干个 pairint,你需要把它输出成合法的 c++ 语法,如 pair pair int int int 输出 pair<pair<int,int>,int>,如果无解输出 Error occurred

Solution

类似一个表达式转化,对于一个 pair 递归两次就行,注意直接用 string 会 TLE,最好先判断一下有没有解。(我这个做法贼蠢,看个乐就行)

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <stack>
#include <string>
using std::cin;
using std::endl;
using std::cout;

const int maxn = 1e6 + 10;

int a[maxn];
char s[10];
int n, m;

void work(int &i)
{
    if (a[i] == 0)
        cout << "int";
    if (a[i] == 1)
    {
        cout << "pair<";
        work(++i);
        cout << ",";
        work(++i);
        cout << ">";
    }
}

bool check(int &i)
{
    if (i == n)
        return a[i] == 0 ? 1 : 0;
    if (i == 1 && a[i] == 0)
        return 0;
    if (a[i] == 0)
        return 1;
    bool tmp = 1;
    if (a[i] == 1)
    {
        tmp = check(++i);
        if (tmp == 0)
            return 0;
        tmp = check(++i);
        if (tmp == 0)
            return 0;
    }
    return 1;
}

int main()
{
    cin >> m;
    while (cin >> s)
        a[++n] = s[0] == 'p' ? 1 : 0;
    if (n != m + m - 1)
    {
        puts("Error occurred");
        return 0;
    }
    int i = 1;
    bool ok = check(i);
    if (!ok || i < n)
    {
        puts("Error occurred");
        return 0;
    }
    i = 1;
    work(i);
    cout << endl;
}

D

Description

给定一个长度为 $n$ 的序列 $a_i$ 和 $k$,求满足出现次数最多的数的出现次数小于 $k$​ 的连续子序列的数目。

Solution

由于是连续的子序列,而且发现选的数越多越可能满足,考虑双指针。

对于每个 $l$,我们找到最小的能满足这个条件的 $r$​,然后后面的就随便选了,把它对答案的贡献加进去,就完事了。

至于怎么找 $r$ 呢,我们用一个 map 存每个数的出现次数就行了。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
using std::cout;
using std::endl;
using std::cin;

const int maxn = 4e5 + 10;
typedef long long ll;

int n, a[maxn], k;
ll ans;
std::map<int, int> mp;

int main()
{
    std::ios::sync_with_stdio(false);
    cin >> n >> k;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    int l = 1, r = 2;        
    mp[a[1]]++;
    for (; l <= n; l++)
    {
        while (r <= n && mp[a[r - 1]] < k)
            mp[a[r]]++, r++;
        if (mp[a[r - 1]] >= k)
            ans += n - r + 2;
        mp[a[l]]--;
    }
    cout << ans << endl;
}

E

Description

给定一个点数为 $n$ 的完全图,从中删除 $m$ 条边,求得到的图的联通块。$n\leq5\times10^5,m\leq10^6$。

Solution

发现一个事情:相对于一个很大的完全图,删除的边数实在少得可怜。量化一下发现最多只能让约 $\dfrac{m}{n}$​​​ 个点成为一个独立的联通块。

这实在是小的可怜。也就是说大部分点实际上是连通的。

考虑到当能删完的时候,$n\approx1500$,那么我们直接找一个在删除的边中出现次数最小的点,把与它相连的点全都取出来当成一个联通块。

剩下的点爆搜一下连通块就行。我的实现非常暴力而且恶心,使用了 map 复杂度大概是 $O(n+(\dfrac{m}{n})^2\log\dfrac{m}{n})$​​。最大的点跑得很慢。

事实上我不知道我这个复杂度到底对不对,反正是卡常卡过去了…

看到有用并查集的做法能做到 $O(n+m)$​,值得一学。。。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
#include <map>
using std::cout;
using std::endl;
using std::cin;

const int maxn = 5e5 + 10;

namespace IO
{
    const int mxsiz = 1 << 20;
    char inbuf[mxsiz], *p1, *p2;
    char outbuf[mxsiz], *op = outbuf;
    struct endio
    {
        endio(){};
        ~endio() { fwrite(outbuf, 1, op - outbuf, stdout); }
    } useless_var;
    inline char gc() { return p1 == p2 && (p2 = (p1 = inbuf) + fread(inbuf, 1, mxsiz, stdin), p1 == p2) ? EOF : *p1++; }
#define isdigit(x) (x >= '0' && x <= '9') // 防止忘记打 <cctype> 头文件
    inline int read()
    {
        int x = 0, f = 1;
        char ch = gc();
        for (; !isdigit(ch); ch = gc())
            if (ch == '-')
                f = -1;
        for (; isdigit(ch); ch = gc())
            x = x * 10 + ch - '0';
        return x * f;
    }
    template <typename T>
    inline void read(T &x)
    {
        x = 0;
        int f = 1;
        char ch = gc();
        for (; !isdigit(ch); ch = gc())
            if (ch == '-')
                f = -1;
        for (; isdigit(ch); ch = gc())
            x = x * 10 + ch - '0';
        x *= f;
    }
#undef isdigit
    inline bool ischar(char x)
    {
        return x >= 'A' && x <= 'z';
    }
    inline char readchar()
    {
        char ch = gc();
        while (!ischar(ch))
            ch = gc();
        return ch;
    }
    inline void push(char ch)
    {
        if (op - outbuf == mxsiz)
            fwrite(outbuf, 1, mxsiz, stdout), op = outbuf;
        *op++ = ch;
    }
    template <typename T>
    inline void work_wt(T x)
    {
        if (x > 9)
            work_wt(x / 10);
        push(x % 10 + '0');
    }
    template <typename T>
    inline void write(T x)
    {
        if (x < 0)
            x = -x, push('-');
        work_wt(x);
    }
    inline void writestr(char *s)
    {
        int n = strlen(s);
        for (int i = 0; i < n; i++)
            push(s[i]);
    }
    inline void endln() { push('\n'); }
    inline void space() { push(' '); }
    template <typename T>
    inline void writeln(T x) { write(x), endln(); }
}
using namespace IO;

int times[maxn], n, mm, tot, used[maxn], arcscc[maxn], m;
std::vector<int> scc[maxn];
std::vector<int> e[maxn], e2[maxn / 10];
std::map<int, int> mp[maxn];
std::vector<int> not_used;
std::vector<int> now;
int p[maxn];
inline void add(int x, int y) { e[x].push_back(y), e[y].push_back(x); }
inline void add2(int x, int y) { e2[x].push_back(y), e2[y].push_back(x); }

void dfs(int x, int q)
{
    arcscc[not_used[x - 1]] = q;
    if (x != m + 1 && not_used[x - 1] != n + 1)
        scc[q].push_back(not_used[x - 1]);
    for (int v : e2[x])
        if (!arcscc[not_used[v - 1]])
            dfs(v, q);
}

int main()
{
    n = read(), mm = read(); // 这里的 mm 是原题的 m,我因为变量名冲突改了一下
    for (int x, y, i = 1; i <= mm; i++)
        x = read(), y = read(), times[x]++, times[y]++, add(x, y);
    int mi = 1e9, pos = 0;
    for (int i = 1; i <= n; i++)
        if (times[i] < mi)
            mi = times[i], pos = i;
    scc[++tot].push_back(pos), used[pos] = 1, arcscc[pos] = 1;
    for (int v : e[pos])
        used[v] = 1;
    for (int i = 1; i <= n; i++)
        if (!used[i] && i != pos)
            scc[tot].push_back(i), arcscc[i] = 1;
        else if (used[i])
            not_used.push_back(i);
    // 以上是找到出现次数最小的点并将与他相连的点删掉
    for (int i : not_used)
    {
        int cnt = 0;
        for (int v : e[i])
            if (arcscc[v] == 0)
                mp[i][v] = mp[v][i] = 1;
            else
                cnt++;
        if (cnt == scc[1].size())
            mp[i][n + 1] = mp[n + 1][i] = 1;
    }
    m = not_used.size(); // 删掉之后原图剩余点数
    not_used.push_back(n + 1); // 除了剩余的点,刚刚删除的点也作为一个联通块加进去
    for (int i = 1; i <= not_used.size(); i++)
        now.push_back(i), p[i] = not_used[i - 1];
    for (int i = 1; i < m + 1; i++)
        for (int j = i + 1; j <= m + 1; j++)
            if (!mp[p[i]][p[j]])
                add2(i, j); // 重新建图
    dfs(m + 1, 1);
    for (int i = 1; i <= m; i++)
        if (!arcscc[not_used[i - 1]])
            dfs(i, ++tot);
    writeln(tot);
    for (int i = 1; i <= tot; i++)
    {
        write(scc[i].size()), space();
        for (int j : scc[i])
            write(j), space();
        endln();
    }
    return 0;
}