牛客小白月赛2 题解

牛客小白月赛2

  • C. 真真假假(签到)
  • E. 是是非非(尼姆博弈)
  • G. 文
  • B. 小马过河
  • D. 虚虚实实(并查集判断欧拉路径)
  • H. 武
  • A. 数字方阵(反魔方阵构造)
  • F. 黑黑白白
  • J. 美(构造)

C. 真真假假(签到)

题解:将所有的头文件弄成一个字符串,然后直接用find()函数搜,简单粗暴,不愧是签到题

#include
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    string s = "algorithmbitsetcctypecerrnoclocalecmathcomplexcstdiocstdlibcstringctimedequeexceptionfstreamfunctionallimitslistmapiomanipiosiosfwdiostreamistreamostreamqueuesetsstreamstackstdexceptstreambufstringutilityvectorcwcharcwctype";
    int _;
    cin >> _;
    while (_--) {
        string str;
        cin >> str;
        if (s.find(str) != s.npos) cout << "Qian" << endl;
        else cout << "Kun" << endl;
    }
    return 0;
}

E. 是是非非(尼姆博弈)

原理:经典尼姆博弈

结论:

  • 先手必败: a 1 a_1 a1$a_2$ . . . ... ...^ a n a_n an = 0
  • 先手必胜: a 1 a_1 a1$a_2$ . . . ... ...^ a n a_n an != 0
#include
using namespace std;
const int N = 100010;
int a[N];
int main()
{
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, _;
    cin >> n >> _;
    int xx = 0;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        xx ^= a[i];
    }
    
    while (_--) {
        int x, y;
        cin >> x >> y;
        int t = a[x] ^ y;
        xx ^= t;
        a[x] = y;
        if (xx) cout << "Kan" << endl;
        else cout << "Li" << endl;
    }
    return 0;
}

G. 文

排名规则:分数优先,分数相同则按名字的字典序排名。

然后就上Code~,最后注意一下输出即可。

#include
using namespace std;
const int N = 100010;
int a[N];
int n, m;
string ans;
int main()
{    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    cin >> n >> m;
    cin >> ans;
    
    int cnt = -1;
    string tname;
    while (m--) {
        string name, a;
        cin >> name;
        cin >> a;
        int len = 0;
        for (int j = 0; j < n; j++) {
            if (a[j] == ans[j]) len++;
        }
        
        if (len > cnt) {
            tname = name;
            cnt = len;
        } else if (len == cnt && tname > name) {
            tname = name;
            cnt = len;
        }
    }
    
    double res = 100;
    cout << tname << endl;
    printf("%.2lf\n", res * cnt * 1.0 / n);
    return 0;
}

B. 小马过河

题解:

P(px, py),U(ux, uy),V(vx, vy)

有三个点的位置,通过U,V两个点的坐标可以求出直线UV的斜率k1

直线UV中:有斜率,有点的坐标,可以通过点斜式写出直线UV的解析式

过P点,作PA⊥UV通过两直线垂直,k1·k2 = -1,求出直线PA的斜率k2

直线PA通过点P(px,py) 那么也可以通过点斜式写出直线PA的解析式

直线UV和直线PA的交点即 要求的垂足。

特殊情况,特殊判断:uxyx,uyvy

常规情况:

直线UV: k = v y − v x u y − u x k=\frac{vy-vx}{uy-ux} k=uyuxvyvx

直线UV通过v点且斜率为k

直线UV的解析式: y − v y = k ( x − v x ) y-vy=k(x-vx) yvy=k(xvx)

过P点,作PA⊥UV通过两直线垂直,则直线UV的斜率为 − 1 k -\frac{1}{k} k1

直线PA的解析式: y − p y = − 1 k ( x − p x ) y-py=-\frac{1}{k}(x-px) ypy=k1(xpx)

联立直线UV和PA:

v y + k ( x − v x ) = p y + [ − 1 k ( x − p x ) ] v_y+k(x-v_x)=p_y+[-\frac{1}{k}(x-p_x)] vy+k(xvx)=py+[k1(xpx)]

v y + k x − k v x = p y − x k + p x k v_y+kx-kv_x=p_y-\frac{x}{k}+\frac{p_x}{k} vy+kxkvx=pykx+kpx

( k + 1 k ) x = p y − v y + k v x + p x k (k+\frac{1}{k})x=p_y-v_y+kv_x+\frac{p_x}{k} (k+k1)x=pyvy+kvx+kpx

x = p y − v y + k v x + p x k k 2 + 1 k x=\frac{p_y-v_y+kv_x+\frac{p_x}{k}}{\frac{k^2+1}{k}} x=kk2+1pyvy+kvx+kpx

x = k 2 v x − k v y + p x + k p y k 2 + 1 x = \frac{k^2v_x-kv_y+p_x+kp_y}{k^2+1} x=k2+1k2vxkvy+px+kpy

y = k ( x − v x ) + v y y = k(x-v_x)+v_y y=k(xvx)+vy

#include
using namespace std;
int main()
{
	ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int _;
    cin >> _;
    while (_--) {
        double px, py, ux, uy, vx, vy;
        cin >> px >> py >> ux >> uy >> vx >> vy;
        if (ux == vx) printf("%.7lf %.7lf\n", ux, py);
        else if (uy == vy) printf("%.7lf %.7lf\n", px, uy);
        else {
            double k = (vy - uy) / (vx - ux);
            double x = (k * k * vx - k * vy + px + k * py) / (k * k + 1);
            double y = k * (x - vx) + vy;
            printf("%.7lf %.7lf\n", x, y);
        }
    }
    return 0;
}

D. 虚虚实实(并查集判断欧拉路径)

相关概念:

欧拉路径:图中的一条路径,这个路径通过图中的每一条边,并且每条边仅通过一次。

欧拉回路:闭合的欧拉路径

欧拉图:包含欧拉回路的图

通俗的解释:欧拉路径即一笔画问题,一笔画的路径叫做欧拉路径,如果最后一笔回到起点,那么这个路径叫做欧拉回路

这道题两个条件:

  1. 度为奇数的数量 ≤ 2:du ≤ 2
  2. 一个根节点:root == 1

满足上述两个条件即可,并查集判断

#include
using namespace std;
const int N = 50;

int f[N], e[N];
void init () {
    memset(e, 0, sizeof e);
    for (int i = 1; i <= N; i++) {
        f[i] = i;
    }
}

int find(int x) {
    return x == f[x] ? x : f[x] = find(f[x]);
}
int main()
{
    int _;
    cin >> _;
    while (_--) {
        init();
        int n, m;
        cin >> n >> m;
        int root = 0;
        int du = 0;
        while (m--) {
            int u, v;
            cin >> u >> v;
            int fu = find(u);
            int fv = find(v);
            if (fu != fv) f[fu] = fv;
            //记录 度
            e[u]++;
            e[v]++;
        }
        for (int i = 1; i <= n; i++) {
            if (e[i] % 2) du++;
            if (f[i] == i) root++;//起点
        }
        if (root == 1 && du <= 2) cout << "Zhen" << endl;
        else cout << "Xun" << endl;
    }
    return 0;
}

H. 武

建图,存每个点的距离,排序,输出第K近的即可

#include
using namespace std;
const int N = 200010;
int n, m, k;
int u, v, p;
int dist[N], h[N], e[N], ne[N], w[N], idx;

void add(int a, int b, int c) {
    w[idx] = c, e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void dfs(int u, int dis, int f) {
    dist[u] = dis;
    for (int i = h[u]; i != -1; i = ne[i]) {
        int j = e[i];
        if (j == f) continue;
        dfs(j, dis + w[i], u);
    }
}
int main()
{
    cin >> n >> m >> k;
    memset(h, -1, sizeof h);
    for (int i = 1; i < n; i++) {
        cin >> u >> v >> p;
        add(u, v, p);
        add(v, u, p);
    }
    
    dfs(m, 0, -1);
    sort(dist + 1, dist + n + 1);
    cout << dist[k + 1] << endl;
    return 0;
}

A. 数字方阵(反魔方阵构造)

从左上角开始,由左到右,由上到下,从1填到 n ( n − 1 ) n(n-1) n(n1),最后一列,从 n ( n − 1 ) + 1 n(n-1)+1 n(n1)+1 n 2 n^2 n2

#include
using namespace std;
const int N = 1010;
int a[N][N];
int main()
{
    int n;
    cin >> n;
    int x = 0, y = n * (n - 1) + 1;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j < n; j++) {
            a[i][j] = ++x;
        }
    }
    
    for (int i = 1; i <= n; i++) a[i][n] = ++x;
    
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cout << a[i][j] << ' ';
        }
        cout << endl;
    }
    return 0;
}

F. 黑黑白白

题解:建树,然后从根节点开始搜索,只要找到一条能赢的路就可以。

#include
using namespace std;
const int N = 500010;
int h[N], e[N], ne[N], idx;
int t;
int n, r;
int u, v;
void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

int dfs(int u, int f) {
    int cur = 0;
    for (int i = h[u]; i != -1; i = ne[i]) {
        int j = e[i];
        if (j == f) continue;
        if (!dfs(j, u)) return 1;
    }
    return 0;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> t;
    while (t--) {
        
        cin >> n >> r;
        memset(h, -1, sizeof h);
        for (int i = 1; i < n; i++) {
            cin >> u >> v;
            add(u, v);
            add(v, u);
        }
        if (dfs(r, -1)) cout << "Gen" << endl;
        else cout << "Dui" << endl;
    }
    return 0;
}

J. 美(构造)

选美大赛,这套题有点阴间

题意,要求选出差值的和最大,依旧是构造题

构造:一个最大,一个最小,一个次大,一个次小 … 以此类推,这样的差之和最大。

举例:1 2 3 4 5

结果:5 1 4 2 3

注意:当 i = 1时,R0 = Rn,所以初始化 ans = abs(b[1] - b[n])

那么就是注意一下下标即可。

#include
#define endl '\n'
#define int long long
using namespace std;
const int N = 100010;
int a[N], b[N];
int n;
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    sort(a + 1, a + n + 1);
    int idx = 0;
    for(int i = 1; i <= n; i += 2) b[i] = a[++idx];
    if (n % 2) {
        for (int i = n - 1; i > 0; i -= 2) b[i] = a[++idx];
    } else {
        for (int i = n; i > 0; i -= 2) b[i] = a[++idx];
    }
//     for (int i = 1; i <= idx; i++) cout << b[i] << ' ';
//     cout << endl;
    int ans = abs(b[1] - b[n]);
    for (int i = 2; i <= n; i++) ans += abs(b[i] - b[i - 1]);
    cout << ans << endl;
    return 0;
}

你可能感兴趣的:(竞赛,题解,欧拉回路,算法,c++)