自我感觉题目难度:

简单:D B F

中等:H G E

进阶:A C I

博客园自带目录()

A

博弈论。

显而易见,对于足够聪明的人,在所有决策中只要存在一个必胜决策,就会直接选择这个策略。

故而可以使用 \(dfs\)

对于一个状态,一共可以从 \(5\)

int a[5] = {1, 2, 3, 5, 8};
bool dp[N];
bool dfs(int k)
{
    if (k == 0) // 无法取数,此状态为必输。
        return 0;
    bool ret = 0; // 判断是否存在必胜策略
    for (int i = 0; i < 5; i++)
    {
        if (k < a[i])
            break;
        ret |= !dfs(k - a[i]);
        // 只需要存在一个必胜状态(对手必输)即可
    }
    return ret;
}

可以发现,此时可能会对同一个状态进行多次搜索,所以可以记忆化(类似斐波那契数列):

int a[5] = {1, 2, 3, 5, 8};
bool dp[N];
bool dfs(int k)
{
    if (dp[k])
        return dp[k];
    if (k == 0)
        return 0;
    bool ret = 0;
    for (int i = 0; i < 5; i++)
    {
        if (k < a[i])
            break;
        ret |= !dfs(k - a[i]);
    }
    return dp[k] = ret;
}

B

签到。

当棉花小熊进行偶数次操作时,灯的状态不会发生改变,而奇数次则会发生改变。

\(m\) 可以直接用 \(string\)

int n;
    string m;
    cin >> n >> m;
    if (m.size() & 1)
        n ^= 1;
    if (n)
        cout << "qwq\n";
    else
        cout << "awa\n";

C

官方说是前缀和。

哥们不懂思维,但是哥们会数据结构。

题目的前半部分相当于是单点查询,区间求值,其中模数为 \(k\)。

可以用线段树或者树状数组解决。

不会线段树的可以移步

而后续操作即为统计每个数出现的次数,用一个桶即可处理。

这个题好像卡时间严重,不能使用 \(map\)。

using i64 = long long;
const int N = 1e6 + 7;
int n, m, mod;
int a[N];
struct Seg_Tree
{
    i64 sum;
    int le, ri, siz;
    int tag;
} T[N << 2];
void pushup(int i)
{
    T[i].sum = (T[i << 1].sum + T[i << 1 | 1].sum) % mod;
}
void build(int i, int l, int r)
{
    T[i].le = l, T[i].ri = r;
    T[i].siz = r - l + 1;
    if (l == r)
    {
        T[i].sum = a[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(i << 1, l, mid);
    build(i << 1 | 1, mid + 1, r);
    pushup(i);
}
void pushdown(int i)
{
    T[i << 1].sum = (T[i << 1].sum + (i64)T[i].tag * T[i << 1].siz % mod) % mod;
    T[i << 1 | 1].sum = (T[i << 1 | 1].sum + (i64)T[i].tag * T[i << 1 | 1].siz % mod) % mod;
    T[i << 1].tag = (T[i << 1].tag + T[i].tag) % mod;
    T[i << 1 | 1].tag = (T[i << 1 | 1].tag + T[i].tag) % mod;
    T[i].tag = 0;
}
void update(int i, int l, int r, int k)
{
    if (r < l)
        return;
    if (l <= T[i].le && T[i].ri <= r)
    {
        T[i].sum = (T[i].sum + (i64)k * T[i].siz % mod) % mod;
        T[i].tag = (T[i].tag + k) % mod;
        return;
    }
    if (T[i].tag)
        pushdown(i);
    int mid = (T[i].le + T[i].ri) >> 1;
    if (l <= mid)
        update(i << 1, l, r, k);
    if (mid < r)
        update(i << 1 | 1, l, r, k);
    pushup(i);
}
int ask(int i, int pos)
{
    if (T[i].le == T[i].ri)
        return T[i].sum;
    if (T[i].tag)
        pushdown(i);
    int mid = (T[i].le + T[i].ri) >> 1;
    if (pos <= mid)
        return ask(i << 1, pos);
    else
        return ask(i << 1 | 1, pos);
}
// map<int, int> tong;
int tong[100000001];
void _()
{
    n = re(), m = re(), mod = re();
    for (int i = 1; i <= n; i++)
        a[i] = re();
    build(1, 1, n);
    for (int i = 1; i <= n; i++)
    {
        a[i] = ask(1, i) % mod;
        update(1, i + 1, min(i + m, n), a[i]);
    }
    int maxn = 0, maxp = -1;
    for (int i = 1; i <= n; i++)
    {
        // wr(a[i], ' ');
        int now = ++tong[a[i]];
        if (now > maxn)
        {
            maxn = now;
            maxp = a[i];
        }
        else if (now == maxn && maxp > a[i])
        {
            maxp = a[i];
        }
    }
    wr(maxp, ' '), wr(maxn, '\n');
}
int main()
{
    int qwq = 1;
    // qwq = re();
    for (int i = 1; i <= qwq; i++)
        _();
    return 0;
}

D

签到。

只需要判断是不是 素数 和是不是 三的倍数 就可以了。

bool is_p(int k)
{
    if (k < 2)
        return 0;
    for (int i = 2; i * i <= k; i++)
        if (k % i == 0)
            return 0;
    return 1;
}
void _()
{
    int n = re();
    bool pd1 = is_p(n);
    bool pd2 = (n % 3 == 0);
    if (pd1 && pd2)
        puts("xiaofeishu");
    else if (pd1)
        puts("xiaoyang");
    else if (pd2)
        puts("xiaomaomi");
    else
        puts("xiaoniaogongzhu");
}

E

挺好的一个思维题。

整体思路是前缀和+二分查找。

首先可以通过前缀和统计在打完第 \(i\)

sum[0] = x;
    for (int i = 1; i <= n; i++)
        sum[i] = sum[i - 1] + a[i] + y;

然后对于每个魔物,通过 \(lower\_bound\) 找到第一个 大于等于 \(t\) 的位置即可,若找不到,输出 Failer

int x = re(), y = re();
    int n = re();
    for (int i = 1; i <= n; i++)
        a[i] = re();
    sum[0] = x;
    for (int i = 1; i <= n; i++)
        sum[i] = sum[i - 1] + a[i] + y;
    int s = re();
    while (s--)
    {
        int t = re();
        int it = lower_bound(sum, sum + n + 1, t) - sum;
        if (it == n + 1)
            puts("Failer");
        else
            wr(it, '\n');
    }

F

签到。

根据 \(i\)

int n = re(), ans = 0;
    for (int i = 1; i <= n; i++)
    {
        int ls = re();
        if (i & 1)
            ans += ls;
        else
            ans -= ls;
    }
    wr(ans, '\n');

G

最大子段和板子,只是非空。

在输入的过程中直接求和 \(sum\),并同时与 \(ans\) 做比较取 \(max\),如果遇到 \(sum<0\) 的情况则说明此时求的 \(sum\) 对于之后的贡献是负的,直接将 \(sum\)

int n = re(), ans = -1e9;
    int sum = 0;
    for (int i = 1; i <= n; i++)
    {
        int ls = re();
        sum += ls;
        ans = max(ans, sum);
        if (sum < 0)
            sum = 0;
    }
    wr(ans, '\n');

H

经典的边问边找。

用一个 \(map\) 储存值所对应的下标,如果对于新输入的 \(k\),存在 \(target-k\),则说明存在情侣,直接输出便可 \(return\)。

本题没有多组测试,若存在多组测试,则需要保证每组数据全部输入完。

int n = re(), m = re();
    for (int i = 1; i <= n; i++)
    {
        int k = re();
        if (T[m - k])
        {
            wr(T[m - k] - 1, ' '), wr(i - 1, '\n');
            return;
        }
        T[k] = i;
    }

I

计算几何,数学题,需要分类讨论。

1. 相离

如图。

哇哦杯题解民间版_前缀和

此时相交的面积为 0。

2. 包含

如图。

哇哦杯题解民间版_ci_02

此时相交的面积为较小的圆的面积。

3. 相交

如图。

哇哦杯题解民间版_i++_03

此时的面积为两段弧之间所包含的面积。

已知 \(O_1,O_2\) 的坐标,可以求出距离 \(d\)。

可以通过中间的绿线分为左右两个弓形部分,能够分别求出。

哇哦杯题解民间版_ci_04

以红色方 \(O_1\) 为例,可以通过 \(r_1,r_2\) 和 \(d\) 求出 \(A\)(余弦公式 \(a^{2}=b^{2}+c^{2}-2bc\cos A\) 和反三角函数 \(\arccos\))。

此时圆心角即为 \(\alpha=2A\)。

哇哦杯题解民间版_前缀和_05

然后红色弓形的面积就是 \(S_{扇形}-S_{\Delta}\),可以通过扇形面积公式和三角形面积公式即可求出。

\[S_{扇形}=\dfrac{1}{2} r^2 \alpha\\ S_{\Delta}=\dfrac{1}{2} r^2\sin{\alpha}\]

struct yuan
{
    double x, y, r;
} r1, r2;
double p2(double x) { return x * x; }
double dis(yuan a, yuan b)
{
    return sqrt(p2(a.x - b.x) + p2(a.y - b.y));
}
void _()
{
    cout << fixed << setprecision(1);
    cin >> r1.x >> r1.y >> r1.r;
    cin >> r2.x >> r2.y >> r2.r;
    double d = dis(r1, r2);
    if (d >= (r1.r + r2.r))
    { // 相离
        cout << 0.0 << '\n';
        return;
    }
    if (d + min(r1.r, r2.r) <= max(r1.r, r2.r))
    { // 包含
        cout << acos(-1) * p2(min(r1.r, r2.r)) << '\n';
        return;
    }

    double alpha1 = 2.0L * acos((p2(r1.r) + p2(d) - p2(r2.r)) / (2.0L * r1.r * d));
    double san1 = p2(r1.r) * sin(alpha1) / 2.0L;
    double shan1 = p2(r1.r) * alpha1 / 2.0L;
    double sub1 = shan1 - san1;

    double alpha2 = 2.0L * acos((p2(r2.r) + p2(d) - p2(r1.r)) / (2.0L * r2.r * d));
    double san2 = p2(r2.r) * sin(alpha2) / 2.0L;
    double shan2 = p2(r2.r) * alpha2 / 2.0L;
    double sub2 = shan2 - san2;
    cout << sub1 + sub2;
}
signed main()
{
    int qwq = 1;
    // qwq = re();
    for (int i = 1; i <= qwq; i++)
        _();
    return 0;
}