【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分 对于其他数据要使用维护凸包(不会 雾)

这里埋个坑

原文地址:https://www.cnblogs.com/dixiao/p/13635755.html