比赛传送门

这真的是小白月赛么……
说好的“题目难度在\(CF\; DIV2\;A\)~\(C\)”呢?
\(D,E\)两题为数学题,所以不想整(并没有歧视数学的意思)

A.华华听月月唱歌

贪心区间覆盖裸题,但细节需要注意

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;
LL read() {
    LL k = 0, f = 1; char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9')
      k = k * 10 + c - 48, c = getchar();
    return k * f;
}
struct zzz {
    int f, t;
}xd[100010];
bool cmp(zzz x, zzz y) {
    if(x.f != y.f)  return x.f < y.f;
    else return x.t > y.t;
}
int main() {
    int n = read(), tot = read();
    for(int i = 1; i <= tot; ++i)
      xd[i].f = read(), xd[i].t = read();
    sort(xd+1, xd+tot+1, cmp);
    int t = 0, ans = 0, pos = 1;
    while(t < n) {
        ++ans;
        int flag = t;
        for(; xd[pos].f <= flag+1 && pos <= tot; ++pos)
          t = max(t, xd[pos].t);
        if(t == flag && flag < n) {
            cout << -1; exit(0);
        }
    }
    cout << ans;
    return 0;
}

B.华华教月月做数学

快速幂+龟速乘

#include<iostream>
#include<cstdio>
#define LL long long
using namespace std;
LL mul(LL b, LL p, LL k) {
    LL ans = 0;
    for(; p; p >>= 1) {
        if(p & 1)
          ans = (ans + b) % k;
        b = (b + b) % k;
    }
    return ans % k;
}
LL pow(LL b, LL p, LL k) {
    LL ans = 1;
    for(; p; p >>= 1) {
        if(p & 1)
          ans = mul(ans, b, k);
        b = mul(b, b, k);
    }
    return ans % k;
}
LL read() {
    LL k = 0, f = 1; char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9')
      k = k * 10 + c - 48, c = getchar();
    return k * f;
}
int main() {
    int t = read();
    while(t--) {
        LL b = read(), p = read(), k = read();
        cout << pow(b, p, k) << endl;
    }
    return 0;
}

还有好写的Python

t = input()
for i in range(0, (int)(t)) :
    s = input().split()
    print(pow(int(s[0]), int(s[1]) , int(s[2])))

E.华华给月月准备礼物

二分答案

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;
LL read() {
    LL k = 0, f = 1; char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9')
      k = k * 10 + c - 48, c = getchar();
    return k * f;
}
int a[200010], l = 1, r;
int n, m;
bool judge(int x) {
    int ans = 0;
    for(int i = 1; i <= n; ++i)
      ans += a[i]/x;
    if(ans < m) return 0;
    else return 1;
}
int main() {
    n = read(), m = read();
    for(int i = 1; i <= n; ++i)
      a[i] = read(), r = max(a[i], r)+1;
    while(l < r) {
        int mid = (l+r) >> 1;
        if(judge(mid)) l = mid+1;
        else r = mid;
    }
    cout << l-1;
    return 0;
}

F.华华开始学信息学

很神仙的一道题,之前从来没见过这种思想

如果暴力使用树状数组来维护,每次修改的时间复杂度为\(O(\frac{n}{x}\log n)\)\(x\)为模数,查询时间复杂度为\(O(n \log n)\)

显然可以发现当\(x\)越大,使用树状数组维护的效率越高,而\(x\)较小的时候使用树状数组是十分不理智的,此时可以使用一个桶来维护,令tong[i]表示模数为\(i\)时加了多少

\(x\)为多少时用树状数组或是桶呢?
显然当\(x\)\(\sqrt{n}\)时效率最高,修改时为\(O(\sqrt{n} \log n)\),查询为\(O(\sqrt{n}+ \log n)\)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#define LL long long
#define lb (i & (-i))
using namespace std;
LL read() {
    LL k = 0, f = 1; char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9')
      k = k * 10 + c - 48, c = getchar();
    return k * f;
}
LL tree[100010 << 1], tong[100010];
int n, m;
void add(int pos, LL x) {
    for(int i = pos; i <= n; i += lb) tree[i] += x;
}
LL sum(int pos) {
    LL ans = 0;
    for(int i = pos; i; i -= lb) ans += tree[i];
    return ans;
}
int main() {
    n = read(), m = read(); int maxx = sqrt(n);
    for(int i = 1; i <= m; ++i) {
        LL opt = read(), x = read(), y = read();
        if(opt == 1) {
            if(x > maxx)
              for(int j = x; j <= n; j += x) add(j, y);
            else tong[x] += y;
        }
        else {
            LL ans = sum(y) - sum(x-1);
            for(int j = 1; j <= maxx; ++j) ans += (y/j - (x-1)/j) * tong[j];
            printf("%lld\n", ans);
        }
    }
    return 0;
}

G.华华对月月的忠诚

虽然\(N\)很大,但答案和\(N\)没有关系,直接求\(a,b\)\(gcd\)即可

H.华华和月月种树

一开始以为是树剖,事实证明我想多了(数据结构的受害者)

动态开点不好维护,我们可以离线操作
先在输入的时候跟着\(1\)操作加点,建出最终的树,然后求此树的\(DFS\)序和每个点的子树大小
这样就把\(2,3\)操作变为了区间加和单点查询,可以用查分树状数组来维护
而对于\(1\)操作,加入新的点时要将这个点上的信息清空

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#define lb (i & (-i))
#define LL long long
using namespace std;
int read() {
    int k = 0; char c = getchar();
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9')
      k = k * 10 + c - 48, c = getchar();
    return k;
}
struct zzz {
    int t, nex;
}e[100010 << 1]; int head[100010], tot;
void add(int x, int y) {
    e[++tot].t = y;
    e[tot].nex = head[x];
    head[x] = tot;
}
struct hhh {
    int opt, x, y;
}que[400010]; int cnt;
int siz[100010], p[100010], num = 1;
void dfs(int pos, int fa) {
    p[pos] = num;
    for(int i = head[pos]; i; i = e[i].nex)
      if(e[i].t != fa)
        ++num, dfs(e[i].t, pos), siz[pos] += siz[e[i].t];
    siz[pos] += 1;
}
int m, n;
LL tree[100010 << 1];
void del(int x) {
    for(int i = x; i <= n; i += lb) tree[i] = 0;
}
void update(int x, int k) {
    for(int i = x; i <= n; i += lb) tree[i] += k;
}
LL sum(int x) {
    LL ans = 0;
    for(int i = x; i; i -= lb) ans += tree[i];
    return ans;
}
int main() {
    m = read(), n = 1;
    for(int i = 1; i <= m; ++i) {
        int x = read(); que[++cnt].opt = x;
        if(x == 1) {
            int y = read()+1; que[cnt].x = y;
            add(++n, y), add(y, n);
        }
        if(x == 2)
          que[cnt].x = read()+1, que[cnt].y = read();
        if(x == 3)
          que[cnt].x = read()+1;
    }
    dfs(1, 0); num = 1;
    for(int i = 1; i <= cnt; ++i) {
        if(que[i].opt == 1) {
            int pos = p[++num];
            int k = sum(pos); update(pos, -k), update(pos+1, k);
        }
        if(que[i].opt == 2) {
            int pos = que[i].x, k = que[i].y;
            update(p[pos], k); update(p[pos]+siz[pos], -k);
        }
        if(que[i].opt == 3) {
            int pos = que[i].x;
            printf("%lld\n", sum(p[pos]));
        }
    }
    return 0;
}

I.华华和月月逛公园

\(Tarjan\)求割边板子题

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct zzz {
    int t, nex;
}e[300010 << 1];
int head[100010], tot;
void add(int x, int y) {
    e[++tot].t = y;
    e[tot].nex = head[x];
    head[x] = tot;
}
int dfn[100010], low[100010], deth, ans;
void tarjan(int pos, int fa) {
    low[pos] = dfn[pos] = ++deth;
    for(int i = head[pos]; i; i = e[i].nex) {
        int to = e[i].t;
        if(!dfn[to]) {
            tarjan(to, pos);
            low[pos] = min(low[pos], low[to]);
            if(low[to] > dfn[pos]) ++ans;
        }
        else if(to != fa)
          low[pos] = min(low[pos], dfn[to]);
    }
}
int read() {
    int k = 0; char c = getchar();
    for (; c < '0' || c > '9';) c = getchar();
    for (; c >= '0' && c <= '9'; c = getchar())
        k = k * 10 + c - 48;
    return k;
}
int main() {
    int n = read(), m = read();
    for(int i = 1; i <= m; ++i) {
        int x = read(), y = read();
        add(x, y), add(y, x);
    }
    for(int i = 1; i <= n; ++i)
      if(!dfn[i]) tarjan(i, i);
    cout << m - ans;
    return 0;
}

J.月月查华华的手机

学了一个新东西:“序列自动机”
对于母串每一位都记一下下一次出现某个字符的位置。匹配的时候从第零位(虚根)开始,如果能一直匹配下去就是\(Yes\),否则就是\(No\)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#define LL long long
#define lb (i & (-i))
using namespace std;
LL read() {
    LL k = 0, f = 1; char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9')
      k = k * 10 + c - 48, c = getchar();
    return k * f;
}
int e[1000010][26];
char A[1000010];
int main() {
    scanf("%s", A+1); int len = strlen(A+1);
    for(int i = len-1; i >= 0; --i) {
        for(int j = 0; j < 26; ++j) e[i][j] = e[i+1][j];
        e[i][A[i+1]-'a'] = i+1;
    }
    int m = read();
    for(int i = 1; i <= m; ++i) {
        scanf("%s", A+1);
        int nex = 0, pos = 1, len = strlen(A+1);
        while(e[nex][A[pos]-'a'] && pos <= len+1)
          nex = e[nex][A[pos]-'a'], ++pos;
        if(pos <= len) printf("No\n");
        else printf("Yes\n");
    }

    return 0;
}
01-06 18:45