zoukankan      html  css  js  c++  java
  • 2021 年百度之星·程序设计大赛

    久违一更,距离上次更新已经半年了。

    这次题目竟然不是按照难度排序

    A. 迷失 (Hdu 6996)

    题目大意

    给定一张无向图,边的边权为(0)(1),在图上随机游走,问从1号点经过k条边到达n号点,经过的边的异或和为1的概率。

    解题思路

    第一眼,图上随机游走,循环概率,点数不超过100,高斯消元,不想写,跳了。

    做完后面的回来看第二眼,哦,恰好经过k条边,设(dp[i][j][k])表示当前第(i)号点,当前边权异或和为(k),经过(j)条边到达(n)号点且边权异或和为(1)的概率。

    忽然看到(k leq 10^6),空间爆了。注意到(dp[i][j][k])总是从(dp[?][j-1][?])转移过来的,于是可以用循环队列压成两维,即(dp[cur][i][k])表示当前状态((j))(dp[cur^1][i][k])表示上一个状态((j - 1)),空间问题解决了,但时间复杂度(O(mk))(O(n^2k))还是爆了时间。

    忽然发现给定一张图,转移方程式是确定且线性的,矩阵快速幂优化就可以了。时间复杂度为(O(n^3log k))

    具体而言,矩阵规模是(200 imes 200),根据转移方程式(dp[i][j][k] = frac{1}{du[i]}sumlimits_{v in (i, v)}dp[v][j-1][k oplus cost[(i,v)])构造即可。

    下面代码中是根据方程式两边乘以了(du[i])构造的。(du[i])表示(i)号点的度。

    结果卡常,中途运算从(long long)改成(int),矩阵乘法减少不必要的取模才过了。

    神奇的代码
    #include <bits/stdc++.h>
    #include <vector>
    using namespace std;
    typedef long long LL;
    
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    const int MO = 998244353;
    const int N = 1e2 + 8;
    const int M = 2e4 + 8;
    
    int head[N], nxt[M], ty[M], cnt[N], to[M];
    
    int inv[N];
    
    int n, m, k, num;
    
    inline int qpow(int a, int b) {
        int ans = 1;
        while(b) {
            if(b & 1) {
                ans = 1ll * ans * a % MO;
            }
            a = 1ll * a * a % MO;
            b = b >> 1;
        }
        return ans;
    }
    
    struct matrix {
        int n, m;
        int data[N * 2][N * 2];
        inline void clear() {
            for (int i = 0; i <= n; i++)
                for (int j = 0; j <= m; j++) data[i][j] = 0;
        }
    }ff, eye;
    
    inline matrix operator *(matrix a, matrix b) {
        matrix qwq;
        qwq.n = a.n;
        qwq.m = b.m;
        qwq.clear();
        LL tmp = 0;
        for (int i = 1; i <= a.n; i++)
            for (int j = 1; j <= b.m; j++){
                tmp = qwq.data[i][j];
                for (int k = 1; k <= a.m; k++){
                    tmp += ((1ll * a.data[i][k] * b.data[k][j]));
                    if (k % 15 == 0)
                        tmp %= MO;
                }
                if (tmp >= MO)
                    tmp %= MO;
                qwq.data[i][j] = tmp;
            }
        return qwq;
    }
    
    inline void add(int u, int v, int w){
        num ++;
        nxt[num] = head[u];
        to[num] = v;
        ty[num] = w;
        head[u] = num;
        num ++;
        nxt[num] = head[v];
        to[num] = u;
        ty[num] = w;
        head[v] = num;
        cnt[u] ++;
        cnt[v] ++;
    }
    
    
    int main(void) {
        for(int i = 1; i <= 105; ++ i)
            inv[i] = qpow(i, MO - 2);
        int kase; read(kase);
        for (int ii = 1; ii <= kase; ii++) {
            read(n);
            read(m);
            read(k);
            num = 0;
            for(int i = 1; i <= n; ++ i){
                head[i] = 0;
                cnt[i] = 0;
            }
            for(int u, v, w, i = 1; i <= m; ++ i){
                read(u);
                read(v);
                read(w);
                add(u, v, w);
            }
            ff.n = ff.m = 2 * n;
            ff.clear();
            for(int i = 1; i <= n; ++ i)
                for(int j = head[i]; j; j = nxt[j]){
                    int v = to[j];
                    int kin = ty[j];
                    for(int s = 0; s <= 1; ++ s){
                        int yuan = i;
                        if (s)
                            yuan += n;
                        int nxt = v;
                        if (s ^ kin)
                            nxt += n;
                        ff.data[yuan][nxt] = inv[cnt[v]];
                    }
                }
            eye.n = eye.m = 2 * n;
            eye.clear();
            for(int i = 1; i <= 2 * n; ++ i)
                eye.data[i][i] = 1;
            while(k){
                if (k & 1)
                    eye = eye * ff;
                ff = ff * ff;
                k >>= 1;
            }
            int ans = eye.data[1][n + n] * 1ll * cnt[n] % MO * inv[cnt[1]] % MO;
            write(ans, '
    ');
        }
        return 0;
    }
    


    B. 愿望幽灵 (Hdu 6997)

    题目大意

    咕咕咕

    解题思路

    神奇的代码
    咕咕咕
    


    C. 鸽子 (Hdu 6998)

    题目大意

    (n)台电脑,第(k)台坏了。(m)次操作,每次操作交换第(u_i)和第(v_i)台电脑,你可以跳过若干次操作。

    问对于每个(j in [1,n]),最终坏的电脑的位置是第(j)台的最小跳过次数是多少。无法实现输出-1。

    解题思路

    (dp[i][j])表示前(i)次操作后,坏的电脑是第(j)台的最小跳过次数。容易发现每次操作只会更改两个(dp)值,即(dp[i])(dp[i-1])只有(dp[i][u_i])(dp[i][v_i])会发生变化,所以复用(dp)数组转移其实是(O(1))的,总时间复杂度为(O(n))

    具体而言,转移时考虑是否跳过本次操作,于是(dp[u_i] = min(dp[v_i], dp[u_i] + 1))(dp[v_i] = min(dp[u_i], dp[v_i] + 1)),注意等式右边的(dp)值是原来的(dp)值,不是本次更新后的(dp)值。注意无法实现的情况。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    const int N = 1e5 + 8;
    
    int n, m, k;
    
    int dp[N];
    
    int main(void) {
        int kase; read(kase);
        for (int ii = 1; ii <= kase; ii++) {
            read(n);
            read(m);
            read(k);
            for(int i = 1; i <= n; ++ i)
                dp[i] = -1;
            dp[k] = 0;
            for(int u, v, i = 1; i <= m; ++ i){
                read(u);
                read(v);
                int su = dp[u];
                int sv = dp[v];
                if (su == -1 && sv == -1)
                    continue;
                if (sv == -1){
                    dp[u] = su + 1;
                    dp[v] = su;
                }
                else if (su == -1){
                    dp[v] = sv + 1;
                    dp[u] = sv;
                }
                else{
                    dp[u] = min(su + 1, sv);
                    dp[v] = min(sv + 1, su);
                }
            }
            for(int i = 1; i <= n; ++ i)
                printf("%d%c", dp[i], i == n ? '
    ' : ' ');
        }
        return 0;
    }
    


    D. 萌新 (Hdu 6999)

    题目大意

    给定两个正整数(a,b),求最小和最大的(c)满足(a mod c = b mod c),且(2 le c le max(a,b))

    不存在则输出(-1)

    解题思路

    假设(ageq b),题意即为(aequiv b mod c)((a-b) equiv 0 mod c)(c|(a-b)),所以最小(c)即为((a-b))的最小质因数,最大(c)即为((a-b))

    (a-b=1)(c)无解$。

    (a=b)时特判(a=1)时无解,否则最小(c)(2),最大(c)(a)

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    int main(void) {
        int kase; read(kase);
        for (int ii = 1; ii <= kase; ii++) {
            int a, b;
            read(a);
            read(b);
            int dis = max({a,b}) - min({a, b});
            if (dis == 1){
                puts("-1 -1");
            }else if (dis == 0){
                if (a == 1){
                    puts("-1 -1");
                }
                else
                    printf("2 %d
    ", a);
            }else{
                int ans = 2;
                int up = sqrt(dis);
                while(dis % ans != 0 && ans <= up)
                    ++ ans;
                if (dis % ans != 0)
                    ans = dis;
                printf("%d %d
    ", ans, dis);
            }
        }
        return 0;
    }
    


    E. 二分 (Hdu 7000)

    题目大意

    (n)个格子,你现在在第(x)个格子,想跳到第(y)个格子。

    每次,如果你当前的格子编号小于(y),你会等概率跳到比当前编号更大的一个格子中,反之则等概率跳到比当前编号更小的一个格子中。

    求期望跳的次数。

    解题思路

    看上去挺有趣的题,结果被A卡常卡得莫得时间思考,咕咕咕。

    神奇的代码
    咕咕咕
    


    F. 毒瘤数据结构题 (Hdu 7001)

    题目大意

    一个长为(n),初始全为(0)的序列,有(n)次操作,每次可以:

    • (1 x) 表示把(x)位置修改为(1)

    • (2 x) 表示查询,如果将(x)位置修改为(1),求最大的(i)满足序列位置(1)(i-1)上的值均为(1)。注意只是如果,实际并不做修改。

    解题思路

    上来先写的题

    其实这并不毒瘤呀,用并查集维护连通性就可以了。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    const int N = 5e6 + 8;
    
    int f[N], n;
    
    int findfa(int x){
        return f[x] == x ? x : f[x] = findfa(f[x]);
    }
    
    int main(void) {
        read(n);
        for(int i = 1; i <= n; ++ i){
            int x, y;
            read(x);
            read(y);
            if (x == 1){
                if (f[y] == 0){
                    f[y] = y;
                    int nxt = findfa(y + 1);
                    if (nxt != 0)
                        f[y] = nxt;
                    int la = findfa(y - 1);
                    if (la != 0)
                        f[la] = f[y];
                }
            }else{
                int ans = findfa(1);
                if (ans == y - 1)
                    ++ ans;
                int nxt = findfa(y + 1);
                if (nxt != 0 && ans >= y)
                    ans = nxt;
                ++ ans;
                write(ans, '
    ');
            }
        }    
        return 0;
    }
    


    G. 流年烹茶 (Hdu 7002)

    题目大意

    咕咕咕

    解题思路

    咕咕咕

    神奇的代码
    咕咕咕
    


    H. 猎人杀 (Hdu 7003)

    题目大意

    (n)个人,其中一个是狼人,其余是猎人。

    每个人都会有一个想干掉的人的编号列表,是一个(1-n)的全排列。

    首先狼人干掉自己编号列表的第一人,记为(target)

    随后,(target)会干掉自己列表中,从左到右第一个活着的人,这人会成为新的(target),重复此步骤。

    如果期间狼人被干掉了,则猎人胜利。

    若某一个猎人被干掉后,场上除了狼人还有另一个猎人活着,则狼人胜利。

    给定一个局面,问你最终谁胜。

    解题思路

    就是个模拟题,不过题意写得晦涩难懂。一开始在疑惑就一晚狼人干掉人吗?不过注意到每个人想干掉的列表是个全排列,那么白天就一定会游戏结束了,狼人不会再干掉第二个人。

    看样例时看到有狼人自刀的情况出现,嗯???

    神奇的代码
    #include <bits/stdc++.h>
    #include <exception>
    #include <vector>
    using namespace std;
    typedef long long LL;
    
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    const int N = 55;
    
    bool dead[N];
    
    int cur[N];
    
    int fav[N][N];
    
    int n, lang;
    
    int main(void) {
        int kase; read(kase);
        for (int ii = 1; ii <= kase; ii++) {
            read(n);
            for(int i = 1; i <= n; ++ i){
                int a;
                read(a);
                if (a == 1)
                    lang = i;
            }
            for(int i = 1; i <= n; ++ i)
                for(int j = 1; j <= n; ++ j){
                    read(fav[i][j]);
                }
            for(int i = 1; i <= n; ++ i){
                dead[i] = false;
                cur[i] = 1;
            }
            int cnt = n;
            int tar = fav[lang][1];
            dead[tar] = true;
            -- cnt;
            while(cnt > 2 && !dead[lang]){
                while(dead[fav[tar][cur[tar]]])
                    ++ cur[tar];
                tar = fav[tar][cur[tar]];
                dead[tar] = true;
                -- cnt;
            }
            if (dead[lang])
                puts("lieren");
            else 
                puts("langren");
        }
        return 0;
    }
    


  • 相关阅读:
    C#的4个基本技巧
    如何使用 Visual C# 加密和解密文件
    C#中的反射
    C#集成FastDFS断点续传
    NETCORE TimeJob定时任务的使用
    C#使用ActiveMQ实例
    hadoop+hbase适合存储海量小图片吗?
    windows环境下elasticsearch安装教程(超详细)
    C#:C#调用WebService
    .NETCore远程调用
  • 原文地址:https://www.cnblogs.com/Lanly/p/15085013.html
Copyright © 2011-2022 走看看