比赛链接:

https://ac.nowcoder.com/acm/contest/35146

B.Capital Program

题意:

\(n\) 座城市,\(n - 1\) 条道路,即一个带权树,选定其中的 \(k\) 个点作为这些城市的首都,求出非首都城市到首都城市的最大距离的最小值。

思路:

容易想到,\(k\) 个城市就是距离树的中心最近的 \(k\) 个点(包括中心)。
所以,先求树的中心。通过树的中心向外扩张,求出最大距离。

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 1e5 + 10;
LL n, k, d[N], c, pa[N], md[N], a[N], ans;
vector <LL> g[N];
void dfs(LL u, LL fa){
    for (auto v : g[u]){
        if (v == fa) continue;
        d[v] = d[u] + 1;
        if (d[v] > d[c]) c = v;
        dfs(v, u);
    }
}
void findC(LL u, LL fa){
    for (auto v : g[u]){
        if (v == fa) continue;
        d[v] = d[u] + 1;
        pa[v] = u;
        if (d[v] > d[c]) c = v;
        findC(v, u);
    }
}
void calD(LL u, LL fa){
    md[u] = d[u];
    for (auto v : g[u]){
        if (v == fa) continue;
        d[v] = d[u] + 1;
        calD(v, u);
        md[u] = max(md[u], md[v]);
    }
}
int main(){
    cin >> n >> k;
    for (int i = 1; i < n; i ++ ){
        LL u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs(1, 0);
    d[c] = 0;
    findC(c, 0);
    LL t = d[c];
    for (int i = 1; i <= (t + 1) / 2; i ++ )
        c = pa[c];
    d[c] = 0;
    calD(c, 0);
    for (int i = 1; i <= n; i ++ )
        a[i] = md[i] - d[i] + 1;
    sort(a + 1, a + n + 1, [](LL a, LL b){
        return a > b;
    });
    for (int i = k + 1; i <= n; i ++ )
        ans = max(ans, a[i]);
    cout << ans << "\n";
    return 0;
}

也可以将叶子节点不断地剪去,直到剩下 \(k\) 个节点,求解最大距离。

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 1e5 + 10;
LL n, k, deg[N], cnt, d[N], ans;
vector <LL> g[N];
void bfs(){
    queue <LL> q;
    for (int i = 1; i <= n; i ++ )
        if (deg[i] == 1){
            d[i] = 1;
            q.push(i);
        }
    while (q.size()){
        LL u = q.front();
        q.pop();
        cnt ++ ;
        if (cnt + k > n) break;
        ans = max(ans, d[u]);
        for (auto v : g[u]){
            d[v] = max(d[u] + 1, d[v]);
            if ( -- deg[v] == 1){
                q.push(v);
            }
        }
    }
    cout << ans << "\n";
}
int main(){
    cin >> n >> k;
    for (int i = 0; i < n - 1; i ++ ){
        LL u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
        deg[u] ++ ;
        deg[v] ++ ;
    }
    bfs();
    return 0;
}

E.Plus

题意:

给定一个 \(n\),找出满足:
1.p 和 q 都是质数
2.1 <= p <= q <= n
3.p ^ q + q ^ p 是质数
的整数对\((p, q)\)的数量。

思路:

只有 (2, 3) 这一对,其它都是不行的。
不会证明。。。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
LL n;
int main(){
    cin >> n;
    if (n <= 2) cout << "0\n";
    else cout << "1\n2 3\n";
    return 0;
}

I.Generator

题意:

有一个随机均匀生成器。现在有一个 \(n\),按下按钮后,它会随机变成 \([1, n]\) 中任意一个数,即所有数的出现都是等概率的。求 \(n\) 按下多少次按钮后变成 1 的期望。

思路

定义 \(f[i]\)\(i\) 变成 1 的期望次数。
\(f[1] = 1\)
\(f[2] = \frac{1}{2} + \frac{1}{2} (f[2] + 1) = 2\)\(\frac{1}{2}\) 的概率变成 1,\(\frac{1}{2} 的概率还是 2,但是变成 2 之后再去转换成 1,就意味着多了一步\)
\(f[3] = \frac{1}{3} + \frac{1}{3} (f[2] + 1) + \frac{1}{3} (f[3] + 1) = 2.5\)
.
.
.
\(f[n] = \frac{1}{n} + \frac{1}{n} (f[2] + 1) + ... + \frac{1}{n} (f[n] + 1)\)

求解 \(f[n]\)
两边都乘上 \(n\)
\(n * f[n] = 1 + f[2] + 1 + ... + f[n] + 1\)
\(n * f[n] = n - 1 + f[1] + f[2] + ... + f[n]\)

定义 \(s_n = f[1] + f[2] + ... + f[n]\)

\(n * f[n] = n - 1 + s_n\)---1式
\((n - 1) * f[n - 1] = n - 2 + s_{n - 1}\)---2式

1式 - 2式得到

\(n * f[n] - (n - 1) * f[n - 1] = 1 + f[n]\)
\(f[n] - f[n - 1] = \frac{1}{n - 1}\)

所以答案从第二项开始就是调和级数的和 + 1
可以预处理 1e6 以内的调和级数,然后大于 1e6 的通过欧拉常数近似替代求。
调和级数求和的推导链接:https://blog.csdn.net/qq_44009311/article/details/99947219

代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n;
double e = 0.5772156649;
double f[N];
int main(){
    cin >> n;
    f[1] = 1;
    for (int i = 2; i <= 1e6; i ++ )
        f[i] = f[i - 1] + 1.0 / i;
    if (n <= 1e6) printf("%.12lf\n", f[n - 1] + 1);
    else printf("%.12lf\n", log(n - 1) + e + 1);
    return 0;
}

K.Maze

题意:

一个 \(n * n\) 的迷宫,'.' 表示可以通过,'*' 表示障碍,不能向一个方向连续走 \(m\) 步以上,问从(1, 1)出发到达 (n, n) 最少的步数是多少。

思路:

容易想到 \(bfs\)
因为沿着同一个方向走 \(m\) 步以上,所以队列中的节点要包含,坐标、方向、沿着这个方向移动的步数、以及移动的总步数。

4 2
.***
.***
.***
....

这一组数据的答案为 10。要回头走一步,然后再向前走。
考虑这一组数据可以发现,对于每一个位置不同方向不同步数的方案都要记录下来,即 \(bfs\) 的标记走过的数组要开四维。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 110;
LL T, n, m, ans[N][N][4][N];
LL dx[] = {-1, 0, 1, 0}, dy[] = {0, -1, 0, 1};
char a[N][N];
struct node{
    LL x, y, d, dt, st;
};
void bfs(){
    queue <node> q;
    for (int i = 0; i < 4; i ++ )
        q.push({1, 1, i, 0, 0});
    while (q.size()){
        node t = q.front();
        q.pop();
        for (int i = 0; i < 4; i ++ ){
            LL nx = t.x + dx[i], ny = t.y + dy[i];
            if (nx < 1 || ny < 1 || nx > n || ny > n || a[nx][ny] == '*') continue;
            LL nt = t.dt + 1;    //按照当前方向移动的步数
            if (i != t.d) nt = 1;
            if (nt > m || ans[nx][ny][i][nt]) continue;
            if (nx == n && ny == n){
                cout << t.st + 1 << "\n";
                return;
            }
            ans[nx][ny][i][nt] = t.st + 1;
            q.push({nx, ny, i, nt, t.st + 1});
        }
    }
    cout << "-1\n";
}
void Clear(){
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
            for (int k = 0; k < 4; k ++ )
                for (int t = 1; t <= n; t ++ )
                    ans[i][j][k][t] = 0;
}
void solve(){
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
            cin >> a[i][j];
    bfs();
    Clear();
}
int main(){
    ios::sync_with_stdio(false);cin.tie(0);
    cin >> T;
    while (T -- )
        solve();
    return 0;
}

L.Polygon

题意:

给定 \(n\) 条边长度,判断能否组成 \(n\) 边形。

思路:

判断最大的那条边是不是比其它所有边之和小就行。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
LL n, x, mx, s;
int main(){
    cin >> n;
    for (int i = 0; i < n; i ++ ){
        cin >> x;
        s += x;
        mx = max(mx, x);
    }
    cout << (s - mx > mx ? "YES\n" : "NO\n");
    return 0;
}
posted on 2022-06-08 21:40  Hamine  阅读(480)  评论(0编辑  收藏  举报