AtCoder Beginner Contest 248 赛时记录
手速场。手速场。手速场。手速场。手速场。
上大分。上大分。上大分。上大分。上大分。
A - Lacked Number
随便标记看看哪个数没出现过,或者拿 \(45\) 去把所有数减掉剩下的数就是。
B - Slimes
暴力乘,算次数
C - Dice Sum
简单 DP。
设 \(f_{i,j}\) 表示前 \(i\) 个数的和为 \(j\) 的方案数。
D - Range Count Query
对每个颜色开一个 vector
,然后存出现的位置。
对于每次询问直接在对应 vector
里 upper_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\),
答案就是 \(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\) 。
所以上式可转化为
把 \(d\) 提到外面得到
所以我们可以枚举外面的 \(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;
}