• 【2020-9-06比赛】 题解【简单游走】【卡牌选取】【合影队形】【公交运输】


    模拟赛直接升天 

    100 + 100 + 100 + 30 秒变 50 + 30 + 0 + 30

    自身的问题还是有点多


    A. 简单游走

    有一张 n 个点, m条边的无向图,点从  1到  n标号。

    时刻 0时,你在结点1 。你需要用最少的时间从结点  1走到结点n 。通过m条边中的每一条都要花一定的时间。

    每个结点会有可能在某些时刻被限制。一个结点 x 在时刻T被限制,意味着这个结点的人在时刻T不能从这个点x走出去。

    你只能在整数时刻进出某个结点,一个结点可以逗留任意非负整数时间。

    现在,请问你最少需要多少时间能从结点 1走到结点n 

    可以看出肯定越早到一个点越优 在这个点上等再到这个点的所有连点 和  到连点后再等待出点 是同一个道理,我们记录每个点的最早到达时间,

    用最早出点时间更新它的连点 跑一遍最短路就行了 同时 数组 需要 开大 且 开始定义的最大值要很大 不然直接wa了。

    #include <iostream>
    #include <cstdio>
    #include <queue>
    #include <algorithm>
    #define f(i, a, b) for (long long i = a; i <= b; i++)
    using namespace std;
    priority_queue<pair<long long, long long> > QAQ;
    long long n, m, k, begi, fina, v, head[100100], d[100100];
    bool tim[5000][5000];
    bool visit[100010];
    struct QWQ {
        long long to, next, value;
    } edge[100100];
    void make(long long a, long long b, long long c) {
        edge[++edge[0].value].to = b;
        edge[edge[0].value].value = c;
        edge[edge[0].value].next = head[a];
        head[a] = edge[0].value;
    }
    void dij() {
        QAQ.push(make_pair(0, 1));
        d[1] = 0;
        while (QAQ.size()) {
            long long num = QAQ.top().second;
            QAQ.pop();
            if(visit[num]) continue;
            visit[num] = 1;
            long long p = d[num];
            while(tim[num][p])p++;
                for (long long i = head[num]; i; i = edge[i].next) {
                    if (p + edge[i].value < d[edge[i].to]) {
                                d[edge[i].to] = p + edge[i].value;
                                QAQ.push(make_pair(-d[edge[i].to], edge[i].to));
                            }
                        }
                    }
        }
    
    int main() {
        freopen("travel.in", "r", stdin);
        freopen("travel.out", "w", stdout);
        scanf("%lld%lld", &n, &m);
        f(i, 1, m) {
            scanf("%lld%lld%lld", &begi, &fina, &v);
            make(begi, fina, v);
            make(fina, begi, v);
        }
        f(i, 1, n) {
            scanf("%lld", &k);
            long long t;
            f(j, 1, k) {
                scanf("%lld", &t);
                tim[i][t] = 1;
            }
            d[i] = 1e16;
        }
        dij();
        printf("%lld", d[n]);
        return 0;
    }
    View Code


    B. 卡牌选取

    校庆志愿者小Z在休息时间和同学们玩卡牌游戏。一共有n张卡牌,每张卡牌上有一个数Ai,每次可以从中选出k张卡牌。一种选取方案的幸运值为这k张卡牌上数的异或和。小Z想知道所有选取方案的幸运值之和除以998244353的余数。
     
    我们考虑使用二进制 对于 二进制的每一位 只有 奇数个1 异或 才是有意义的 所以我们只要对每一位的数字统计 n 个 0,1 有多少种 k 个 数的组合有奇数个1
     
    (设此为所有的数的的和为sum)此位为第i位 选出 j 个 1 则剩下 k - j 个 0 

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const long long MOD = 998244353;
    long long jc[1000000],inv[1000000];
    long long n,k,sum,num[1000000],cn[1000000];
    long long pow(long long a,long long b)
    {
         long long ans = 1;
         while(b)
         {
            if(b & 1)
             ans = (ans * a )%MOD;
             a =( a * a )%MOD;
             b = b >> 1;
         }
         return ans;
    }
    long long C(long long n,long long m)
    {
        if(m > n)
        return 0;
        return (jc[n] % MOD * inv[m] % MOD * inv[n-m] % MOD) % MOD; 
    }
    int main()
    {
        freopen("card.in","r",stdin);
        freopen("card.out","w",stdout);
        scanf("%lld%lld",&n,&k);
        for(long long i = 1; i <= n; i++)
        {
            scanf("%lld",&num[i]);
            for(long long j = 0 ;j <= 30; j++)
            if(num[i] & (1 << j)) cn[j] ++;
        }
        jc[0] = inv[0] = jc[1] = 1;
        for(long long i = 2; i <= n ;i++)
        jc[i] = (i%MOD * jc[i - 1]%MOD)%MOD;
        inv[n] = pow (jc[n],MOD - 2)%MOD;
        for(long long i = n ; i >= 1; i--)
        inv[i - 1] = (inv[i]%MOD * (i)%MOD)%MOD;
        inv[1] = 1;
        for(long long i = 0; i <= 30 ; i++)
        {
            long long s = 0;
            for(long long j = 1 ; j <= k; j += 2)
            s += ((1LL)*C(cn[i],j)%MOD * C(n - cn[i],k - j)%MOD)%MOD ;
            sum += ((1LL<<i) %MOD * s%MOD)%MOD; 
        }
        cout<<sum%MOD;
    }
    View C

    我们将所有的要求变成一颗树,若a要求在b前面,那么a<-b连一条边,若有x没有要求,连一条0->x的边

    先判断有没有环,有就直接输出0

    没有环,就从0开始dfs,当每个子树的方案都已经求出来后就可以用乘法原理合并了

    其实就是每次将一堆节点插入到另一堆中,在乘上子树自己的方案数

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <vector>
    #include <cstdio>
    #include <cmath>
    #include <queue>
    using namespace std;
    
    typedef long long LL;
    
    inline const LL Get_Int() {
        LL num = 0, bj = 1;
        char x = getchar();
        while (x < '0' || x > '9') {
            if (x == '-')
                bj = -1;
            x = getchar();
        }
        while (x >= '0' && x <= '9') {
            num = num * 10 + x - '0';
            x = getchar();
        }
        return num * bj;
    }
    
    const int maxn = 200005;
    int t, n, m, Size[maxn], InDegree[maxn];
    LL f[maxn], fac[maxn], inv[maxn], mod;
    bool bj = 0, vst[maxn];
    vector<int> edges[maxn];
    
    void Clear() {
        memset(InDegree, 0, sizeof(InDegree));
        memset(vst, 0, sizeof(vst));
        bj = 0;
        for (int i = 0; i <= n; i++) edges[i].clear();
    }
    
    void AddEdge(int x, int y) { edges[x].push_back(y); }
    
    void Dfs(int Now) {
        vst[Now] = 1;
        for (int i = 0; i < edges[Now].size(); i++) {
            int Next = edges[Now][i];
            if (vst[Next]) {
                bj = 1;
                return;
            }
            Dfs(Next);
            if (bj)
                return;
        }
    }
    
    LL C(LL a, LL b) { return fac[a] * inv[b] % mod * inv[a - b] % mod; }
    
    void TreeDp(int Now) {
        f[Now] = 1;
        Size[Now] = 0;
        for (int i = 0; i < edges[Now].size(); i++) {
            int Next = edges[Now][i];
            TreeDp(Next);
            Size[Now] += Size[Next];
            f[Now] = f[Now] * C(Size[Now], Size[Next]) % mod * f[Next] % mod;
        }
        Size[Now]++;
    }
    
    LL Quick_Pow(LL a, LL b) {
        LL ans = 1;
        while (b) {
            if (b & 1)
                ans = ans * a % mod;
            a = a * a % mod;
            b >>= 1;
        }
        return ans;
    }
    
    int main() {
        freopen("photo.in", "r", stdin);
        freopen("photo.out", "w", stdout);
        t = Get_Int();
        while (t--) {
            n = Get_Int();
            m = Get_Int();
            mod = Get_Int();
            Clear();
            fac[0] = inv[0] = 1;
            for (int i = 1; i <= n + 1; i++) fac[i] = fac[i - 1] * i % mod;
            inv[n + 1] = Quick_Pow(fac[n + 1], mod - 2);
            for (int i = n; i >= 1; i--) inv[i] = inv[i + 1] * (i + 1) % mod;
            for (int i = 1; i <= m; i++) {
                int x = Get_Int(), y = Get_Int();
                AddEdge(y, x);
                InDegree[x]++;
            }
            for (int i = 1; i <= n; i++)
                if (!InDegree[i])
                    AddEdge(0, i);
            for (int i = 0; i <= n; i++)
                if (!vst[i])
                    Dfs(i);
            if (bj) {
                puts("0");
                continue;
            }
            TreeDp(0);
            printf("%lld
    ", f[0]);
        }
        return 0;
    }
    View Code

    对于第一档数据我们考虑DP DP方程并不难写

    #include<iostream>
    #include<cstdio>
    #define f(i,a,b) for(long long i = a; i <= b; i++)
    #define d(i,a,b) for(long long i = a; i >= b; i--)
    using namespace std;
    long long n,maxx;
    long long ans[1000010],c[1000010],v[1000010];
    int main()
    {
        freopen("bus.in","r",stdin);
        freopen("bus.out","w",stdout);
        scanf("%lld%lld",&n,&maxx);
        f(i,0,n - 1)
        {
            scanf("%lld%lld",&c[i],&v[i]);
            ans[i] = 0x7f7f7f;
        }
        ans[n] = 0x7f7f7f;
        ans[0] = 0;
        f(i,1,n)
        {
            d(j,i-1,0)
            if((i - j)%c[j] == 0)
            ans[i] = min(ans[i],ans[j] + (i - j)/c[j]*v[j]);
            if(ans[i] == 0x7f7f7f)
            printf("-1 ");
            else
            printf("%lld ",ans[i]);
        }
    }
    View Code

    我们对于第三档数据 考虑贪心从v最小的转移

    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define re register
    
    inline int read() {
        int num = 0, sym = 1;
        char ch = getchar();
        while (ch < '0' || ch > '9') {
            if (ch == '-')
                sym = -1;
            ch = getchar();
        }
        while ('0' <= ch && ch <= '9') {
            num = (num << 1) + (num << 3) + (ch ^ 48);
            ch = getchar();
        }
        return num * sym;
    }
    
    const int maxn = 1e6 + 10;
    
    int n, C, c[maxn], v[maxn];
    ll f[maxn], ans;
    
    int main() {
        freopen("bus.in", "r", stdin);
        freopen("bus.out", "w", stdout);
        n = read();
        C = read();
        for (int i = 0; i < n; i++)
            c[i] = read(), v[i] = read();
        if (C == 1) {
            int nu = v[0];
            for (int i = 1; i <= n; i++) {
                ans += nu;
                nu = min(nu, v[i]);
                printf("%lld ", ans);
            }
            return 0;
        }
        memset(f, 0x3f3f3f3f, sizeof(f));
        f[0] = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j < i; j++) {
                if ((i - j) % c[j] == 0 && f[j]!= 0x3f3f3f3f) {
                    f[i] = min(f[i], f[j] + ((i - j) / c[j]) * v[j]);
                }
            }
            if (f[i] >= 0x3f3f3f3f)
                printf("-1 ");
            else
                printf("%lld ", f[i]);
        }
        return 0;
    }
    View Code

    这样可以拿50分 对于其他数据要使用维护凸包(不会 雾)

    这里埋个坑

  • 相关阅读:
    VMWare虚拟机15.X局域网网络配置(修改网卡)
    Redis的中并发问题的解决方案小结
    搭建一个dubbo+zookeeper平台
    react与redux学习资料的整理
    如何利用python+Selenium对登录的验证码进行验证?
    (基础篇)哪些面试遇见的坑——面试必看基础题
    测试员小白必经之路----常见的linux操作命令
    测试员小白必经之路----常见的Dos命令
    Elastcisearch.Nest 7.x 系列`伪`官方翻译:通过 NEST 来快捷使用 Elasticsearch
    StackExchange.Redis 系列 1:基础使用
  • 原文地址:https://www.cnblogs.com/dixiao/p/13635755.html
Copyright © 2020-2023  润新知