AtCoder Beginner Contest 248 赛时记录

手速场。手速场。手速场。手速场。手速场。

上大分。上大分。上大分。上大分。上大分。

A - Lacked Number

随便标记看看哪个数没出现过,或者拿 \(45\) 去把所有数减掉剩下的数就是。

B - Slimes

暴力乘,算次数

C - Dice Sum

简单 DP。

\(f_{i,j}\) 表示前 \(i\) 个数的和为 \(j\) 的方案数。

\[f_{i,j} = \sum_{k=1}^{m} f_{i-1,j-k} \]

D - Range Count Query

对每个颜色开一个 vector,然后存出现的位置。

对于每次询问直接在对应 vectorupper_bound 出和合法的左右端点相减就能得到答案。

signed main() {
    n = read();
    for(int i = 1; i <= n; ++i) {
        a[i] = read();
        b[a[i]].push_back(i);
    }
    Q = read();
    for(int i = 1, l, r, x; i <= Q; ++i) {
        l = read(), r = read(), x = read();
        int L = upper_bound(b[x].begin(), b[x].end(), l - 1) - b[x].begin() - 1;
        int R = upper_bound(b[x].begin(), b[x].end(), r) - b[x].begin() - 1;
        cout << R - L << "\n";
    }
	return 0;
}

E - K-colinear Line

枚举两个点确定一条直线,然后枚举第三个点判断有多少点在这个直线上。

判断直线的方式我们采用向量的形式,\(i,j,k\) 三个点可以得到两个向量 \((a,b) = (x_i-x_k,y_i-y_k), (c,d) = (x_j-x_k,y_j-y_k)\),那如果 \(ad=bc\) 就说明这三个点在一条直线上。

然后发现这样会算重很多。

对于一条有 \(K\) 个点的直线来说,会被算重 \(\frac{K(K-1)}{2}\),所以统计 \(K\) 个点的直线有几条,计算贡献的时候除以 \(\frac{K(K-1)}{2}\) 就好了。

bool Check(int i, int j, int k) {
    int ax = x[i] - x[k], ay = y[i] - y[k];
    int bx = x[j] - x[k], by = y[j] - y[k];
    return ax * by == ay * bx;
}

signed main() {
    n = read(), K = read();
    if(K == 1) { return puts("Infinity"), 0; }
    for(int i = 1; i <= n; ++i) x[i] = read(), y[i] = read();
    for(int i = 1; i <= n; ++i) {
        for(int j = i + 1; j <= n; ++j) {
            int res = 2;
            for(int k = 1; k <= n; ++k) {
                if(k == i || k == j) continue;
                if(Check(i, j, k)) res ++;
            }
            cnt[res] ++;
        }
    }
    for(int i = K; i <= n; ++i) ans += cnt[i] * 2 / i / (i - 1);
    cout << ans << "\n";
	return 0;
}

F - Keep Connect

考虑直接 DP。

一开始的一个想法是设 \(f_{i,j,0/1}\) 表示前 \(i\) 列删了 \(j\) 条边第 \(i\) 列中间那条边删没删,然后发现你转移的时候不知道最后的结果合不合法,因为不知道连通性,所以把意义换一下,改成表示前 \(i\) 列删了 \(j\) 条边是否联通的方案数。

然后在打草纸上画一下所有情况进行转移即可。

最后可以得出:

初始化 \(f_{1,0,1} = f_{1,1,0} = 1\)

\[\begin{aligned} f_{i,j,1} & = f_{i-1,j,1} + 3 f_{i-1,j-1,1} + f_{i-1,j,0} \\ f_{i,j,0} & = f_{i-1,j-1,0} + 2 f_{i-1,j-2,1} \end{aligned} \]

答案就是 \(f_{n,i,1}\)

signed main() {
    n = read(), P = read();
    f[1][0][1] = 1, f[1][1][0] = 1;
    for(int i = 1; i <= n; ++i) {
        for(int j = 0; j <= n - 1; ++j) {
            f[i][j][1] += f[i - 1][j][1];
            if(j > 0) f[i][j][1] += f[i - 1][j - 1][1] * 3;
            f[i][j][1] += f[i - 1][j][0];
            if(j > 0) f[i][j][0] += f[i - 1][j - 1][0];
            if(j > 1) f[i][j][0] += f[i - 1][j - 2][1] * 2;
            f[i][j][1] %= P, f[i][j][0] %= P;
        }
    }
    for(int i = 1; i <= n - 1; ++i) {
        cout << f[n][i][1] << " ";
    }
	return 0;
}

G - GCD cost on the tree

给你一棵大小为 \(n\) 的树,点有点权,求

\[\sum_{i=1}^{n} \sum_{j=i+1}^{n} dis(i,j) \times \gcd(a_{p_k}) \]

\(dis(i,j)\) 表示 \(i \to j\) 的路径长,\(\gcd(a_{p_k})\) 表示路径经过的点的权值的 \(\gcd\)

\(2 \le n \le 10^5, 1 \le a_i \le 10^5\)

众所周知 \(\sum_{d | n} \varphi (d) = n\)

所以上式可转化为

\[\sum_{i=1}^{n} \sum_{j=i+1}^{n} dis(i,j) \times \sum_{d | \gcd(a_{p_k})} \varphi(d) \]

\(d\) 提到外面得到

\[\sum_{d} \varphi(d) \sum_{i=1}^{n} \sum_{j=i+1}^{n} dis(i,j) \times [d | \gcd(a_{p_k})] \\ \]

所以我们可以枚举外面的 \(d\),然后只取 \(d | a_i\) 的点,在这些点所构成的森林中计算出两两距离的和,就可以得到后面这一块的贡献,两两距离的和可以通过两次 dfs 得到。

寻找 \(a_i\) 的因数的复杂度是 \(\sqrt {a_i}\) ,因为 \(10^5\) 内的数的因子最多有 \(128\) 个,所以枚举 \(d\) 求贡献的复杂度最多是 \(128n\) 的。

所以总复杂度为 \(n \sqrt {a_i} + 128n\),可以通过。

#include<bits/stdc++.h>
#define LL long long
#define int long long
#define orz cout << "tyy YYDS!!!\n"
using namespace std;
const int MAXN = 2e5 + 10;
const int INF = 1e9 + 7;
const int mod = 998244353;

int n, D, res, ans = 0;
int a[MAXN], siz[MAXN];
vector<int> E[MAXN], V[MAXN];
bool vis[MAXN];

int read() {
	int s = 0, f = 0; char ch = getchar();
	while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
	return f ? -s : s;
}

void dfs1(int u, int fa) {
    vis[u] = true, siz[u] = 1;
    for(auto v : E[u]) {
        if(v == fa) continue;
        if(a[v] % D) continue;
        dfs1(v, u);
        siz[u] += siz[v];
    }
}

void dfs2(int u, int fa, int sz) {
    vis[u] = false;
    int lst = sz - siz[u] + 1;
    res += lst;
    for(auto v : E[u]) {
        if(v == fa || a[v] % D) continue;
        res = (res + lst * siz[v] % mod) % mod;
        lst += siz[v];
    }
    for(auto v : E[u]) {
        if(v == fa || a[v] % D) continue;
        dfs2(v, u, sz);
    }
}

int Phi(int x) {
    int res = 1;
    for(int i = 2; i * i <= x; ++i) {
        if(x % i) continue;
        res *= (i - 1);
        x /= i;
        while(x % i == 0) res *= i, x /= i;
    }
    if(x > 1) res = res * (x - 1);
    return res;
}

signed main() {
    n = read();
    for(int i = 1; i <= n; ++i) a[i] = read();
    for(int i = 1; i <= n; ++i) {
        int x = a[i];
        for(int j = 1; j * j <= a[i]; ++j) {
            if(a[i] % j) continue;
            V[j].push_back(i);
            if(j * j != a[i]) V[a[i] / j].push_back(i);
        }
    }
    for(int i = 1, u, v; i < n; ++i) { 
        u = read(), v = read();
        E[u].push_back(v), E[v].push_back(u);
    }
    for(D = 1; D <= 100000; ++D) {
        res = 0;
        for(auto u : V[D]) if(!vis[u]) dfs1(u, 0);
        for(auto u : V[D]) if(vis[u]) dfs2(u, 0, siz[u]); 
        ans = (ans + Phi(D) * res % mod) % mod;
    }
    for(int i = 1; i <= n; ++i) ans = (ans - a[i] + mod) % mod;
    printf("%lld\n", ans);
	return 0;
}
posted @ 2022-04-17 06:43  Suzt_ilymtics  阅读(193)  评论(1编辑  收藏  举报