2020牛客暑期多校训练营(第一场)F、H、I、J题解及补题

2020牛客暑期多校训练营(第一场)题解及补题

比赛过程

这场比赛总的来说我们发挥的一般,F的签到不够快,J的公式也是靠OEIS推的。由于比赛是线上的,互相之间的交流也相对比较困难。总的来说我们能力上确实还有所欠缺,之间的配合和交流也有待加强。

题解

F

题意

给出两个串,各自首尾相接重复循环无限次,判断两者大小。

解法

官方的解法中,用到了Periodicity Lemma定理,即两个串s,t,若两者无限循环的前 ∣ s ∣ + ∣ t ∣ − g c d ( ∣ s ∣ , ∣ t ∣ ) |s|+|t|-gcd(|s|,|t|) s+tgcd(s,t)位都相同,则两个串相等。用这个定理的话这题就很裸了。另外似乎直接扩两倍也能得正解。

代码
#pragma region
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (long long i = a; i <= n; ++i)
#define per(i, a, n) for (long long i = n; i >= a; --i)
#define debug() W("     ");
template <class T>
void _R(T &x) { cin >> x; }
void _R(int &x) { scanf("%d", &x); }
void _R(int64_t &x) { scanf("%lld", &x); }
void _R(double &x) { scanf("%lf", &x); }
void _R(char &x) { x = getchar(); }
void _R(char *x) { scanf("%s", x); }
void R() {}
template <class T, class... U>
void R(T &head, U &... tail) { _R(head), R(tail...); }
template <class T>
void _W(const T &x) { cout << x; }
void _W(const int &x) { printf("%d", x); }
void _W(const int64_t &x) { printf("%lld", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
template <class T, class U>
void _W(const pair<T, U> &x) { _W(x.F), putchar(' '), _W(x.S); }
template <class T>
void _W(const vector<T> &x) {
    for (auto i = x.begin(); i != x.end(); _W(*i++))
        if (i != x.cbegin()) putchar(' ');
}
void W() {}
template <class T, class... U>
void W(const T &head, const U &... tail) { _W(head), putchar(sizeof...(tail) ? ' ' : '\n'), W(tail...); }
#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
template <typename T>
void read(T &x) {
    x = 0;
    char ch = getchar();
    ll f = 1;
    while (!isdigit(ch)) {
        if (ch == '-') f *= -1;
        ch = getchar();
    }
    while (isdigit(ch)) {
        x = x * 10 + ch - 48;
        ch = getchar();
    }
    x *= f;
}
#pragma endregion
const int maxn = 55;
int main() {
    IO;
    string s, t;
    while (cin >> s >> t) {
        s += s, t += t;
        int flag = 0;
        rep(i, 0, max(s.length(), t.length()) * 2 - 1) {
            if (s[i % s.length()] != t[i % t.length()]) {
                cout << (s[i % s.length()] < t[i % t.length()] ? "<\n" : ">\n");
                flag = 1;
                break;
            }
        }
        if (!flag) cout << "=\n";
    }
}

H

题意

给出一个n点m边的有向图,每条边都有各自费用 c o s t i cost_i costi ,给出 q q q 个询问,问当所有边流量设置为 u i / v i u_i/v_i ui/vi 之后,从 1 1 1 n n n 的流量为 1 1 1 的最小花费。

解法

首先,由于每条边的容量相同,所以每条增广路的最大容量相同(即为最大容量)。我们可以预处理出每条增广路的花费。然后对于每个询问,可以转化为每条路容量 u u u ,所需最大流 v v v v = a ∗ u + b ( b < u ) v=a*u+b(b < u) v=au+b(b<u) ,然后取预处理中前 a a a 条所有容量和第 a + 1 a+1 a+1 b b b

代码
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")

#include <bits/stdc++.h>
//#include <ext/pb_ds/assoc_container.hpp>
//#include <ext/pb_ds/tree_policy.hpp>
//#include <ext/pb_ds/priority_queue.hpp>

//using namespace __gnu_pbds;
using namespace std;

#define ll long long
#define ld long double
#define ull unsigned long long
#define mst(a, b) memset((a), (b), sizeof(a))
#define mp(a, b) make_pair(a, b)
#define pi acos(-1)
#define endl '\n'
#define pii pair<int, int>
#define pll pair<ll, ll>
#define pdd pair<double, double>
#define vi vector<int>
#define vl vector<ll>
#define pb push_back
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
#define lowbit(x) x &(-x)
#define all(x) (x).begin(), (x).end()
#define sf(x) scanf("%d", &x)
#define pf(x) printf("%d", x)
#define debug(x) cout << x << endl
#define mod(x) (x % mod + mod) % mod

template <typename T>
void read(T &x)
{
    x = 0;
    char ch = getchar();
    ll f = 1;
    while (!isdigit(ch))
    {
        if (ch == '-')
            f *= -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = x * 10 + ch - 48;
        ch = getchar();
    }
    x *= f;
}

const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-8;
const int maxn = 2e5 + 7;
const int maxm = 1e5 + 7;
const int mod = 1e9 + 7;

#define IO ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
//template<typename T> using ordered_set = tree<T,null_type,less<T>,rb_tree_tag,tree_order_statistics_node_update>;

struct edge
{
    ll nex, v, cost, flow;
} edges[maxm];
ll head[maxn], cnt = 1;
int n, m;
map<ll, ll> mp;
void addedge(ll u, ll v, ll cost, ll flow)
{
    edges[++cnt] = {head[u], v, cost, flow};
    head[u] = cnt;
}
ll inque[maxn];
ll dis[maxn], flow[maxn], pre[maxn], last[maxn];
bool spfa(int s, int t)
{
    queue<ll> q;
    mst(dis, INF);
    mst(flow, INF);
    mst(inque, 0);
    q.push(s);
    inque[s] = 1;
    dis[s] = 0;
    pre[t] = -1;
    while (!q.empty())
    {
        ll u = q.front();
        q.pop();
        inque[u] = 0;
        for (ll i = head[u]; i; i = edges[i].nex)
        {
            ll v = edges[i].v;
            if (dis[v] > dis[u] + edges[i].cost && edges[i].flow)
            {
                dis[v] = dis[u] + edges[i].cost;
                pre[v] = u;
                last[v] = i;
                flow[v] = min(flow[u], edges[i].flow);
                if (!inque[v])
                {
                    q.push(v);
                    inque[v] = 1;
                }
            }
        }
    }
    if (pre[t] != -1)
        return true;
    return false;
}
ll maxflow, mincost;
void mcmf(int s, int t)
{
    maxflow = 0;
    mincost = 0;
    while (spfa(s, t))
    {
        int u = t;
        maxflow += flow[t];
        mincost += flow[t] * dis[t];
        mp[flow[t] * dis[t]] += flow[t];
        while (u != s)
        {
            edges[last[u]].flow -= flow[t];
            edges[last[u] ^ 1].flow += flow[t];
            u = pre[u];
        }
    }
    return;
}
int main()
{
    while (scanf("%lld%lld",&n,&m)!=EOF)
    {
        mst(head, 0);
        cnt = 1;
        mp.clear();
        mst(last, 0);
        ll u, v, c;
        for (int i = 1; i <= m; ++i)
        {
            scanf("%lld%lld%lld",&u,&v,&c);
            addedge(u, v, c, 1);
            addedge(v, u, -c, 0);
        }
        int q;
        mcmf(1, n);
        scanf("%d",&q);
        while (q--)
        {
            scanf("%lld%lld",&u,&v);
            ll x = 0, y = v;
            if (maxflow * u < v)
                printf("NaN\n");
            else
            {
                for (auto &i : mp)
                {
                    if (v > i.second * u)
                    {
                        v -= i.second * u;
                        x += i.first * u;
                    }
                    else
                    {
                        x += i.first * v / i.second;
                        break;
                    }
                }
                ll g = __gcd(x, y);
                printf("%lld/%lld\n",x/g,y/g);
            }
        }
    }
    return 0;
}

I

题意

给出n个点和m条边组成的无向图,无自环,无重边。问是否能通过删边使得每个点的度为所要求 d i d_i di

解法

现场写的时候,首先记录既有图的每个点的度 i n [ i ] in[i] in[i],然后可以发现若 i n [ i ] < d [ i ] in[i] < d[i] in[i]<d[i] 无解,若 i n [ i ] = d [ i ] in[i]=d[i] in[i]=d[i] 则此点的边不变,由此,这些点不在我们决策范围内,可以建立一个只包含可删边的点的子图,而后由一般图的匹配来做,没想到的是搜一般图匹配算法板子的时候找到原题了。网络流听说是个假算法,那就不补网络流方法了。

代码
#include <stdio.h>

#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <map>
#include <set>
#include <string>
#include <vector>
using namespace std;

const int maxn = 600;
bool vis[maxn], flag1[maxn], flag2[maxn];
int match[maxn], fa[maxn], base[maxn], Q[maxn], adj[maxn][maxn];
int n, head, tail, start, finish;
inline void Contract(int x, int y) {
    memset(vis, 0, sizeof(vis));
    memset(flag1, 0, sizeof(flag1));
    int lca, i;
    for (i = x; i; i = fa[match[i]]) {
        i = base[i];
        vis[i] = 1;
    }
    for (i = y; i; i = fa[match[i]]) {
        i = base[i];
        if (vis[i]) {
            lca = i;
            break;
        }
    }
    for (i = x; base[i] != lca; i = fa[match[i]]) {
        if (base[fa[match[i]]] != lca) fa[fa[match[i]]] = match[i];
        flag1[base[i]] = 1;
        flag1[base[match[i]]] = 1;
    }
    for (i = y; base[i] != lca; i = fa[match[i]]) {
        if (base[fa[match[i]]] != lca) fa[fa[match[i]]] = match[i];
        flag1[base[i]] = 1;
        flag1[base[match[i]]] = 1;
    }
    if (base[x] != lca) fa[x] = y;
    if (base[y] != lca) fa[y] = x;
    for (i = 1; i <= n; ++i) {
        if (flag1[base[i]]) {
            base[i] = lca;
            if (!flag2[i]) {
                Q[++tail] = i;
                flag2[i] = 1;
            }
        }
    }
}
inline void Change() {
    int x, y, z;
    z = finish;
    while (z) {
        y = fa[z];
        x = match[y];
        match[y] = z;
        match[z] = y;
        z = x;
    }
}
inline void FindAugmentPath() {
    memset(fa, 0, sizeof(fa));
    memset(flag2, 0, sizeof(flag2));
    for (int i = 1; i <= n; ++i) base[i] = i;
    head = 0;
    tail = 1;
    Q[1] = start;
    flag2[start] = 1;
    while (head != tail) {
        int x = Q[++head];
        for (int y = 1; y <= n; ++y) {
            if (adj[x][y] && base[x] != base[y] && match[x] != y)
                if (start == y || match[y] && fa[match[y]])
                    Contract(x, y);
                else if (!fa[y]) {
                    fa[y] = x;
                    if (match[y]) {
                        Q[++tail] = match[y];
                        flag2[match[y]] = 1;

                    } else {
                        finish = y;
                        Change();
                        return;
                    }
                }
        }
    }
}
int deg[maxn], D[maxn], M, N;
pair<int, int> edge[maxn], id[maxn];
int main() {
    while (~scanf("%d%d", &N, &M)) {
        memset(deg, 0, sizeof(deg));
        memset(adj, 0, sizeof(adj));
        for (int i = 0; i < N; ++i) {
            scanf("%d", &D[i]);
        }
        for (int i = 0; i < M; ++i) {
            int u, v;
            scanf("%d%d", &u, &v);
            u--;
            v--;
            edge[i] = make_pair(u, v);
            deg[u]++;
            deg[v]++;
        }

        bool flag = true;
        int cnt = 1;
        for (int i = 0; i < maxn; ++i) id[i] = make_pair(-1, -1);
        for (int i = 0; i < N; ++i)
            if (deg[i] < D[i]) {
                flag = false;
                break;
            }
        if (!flag) {
            puts("No");
            continue;
        }
        for (int i = 0; i < M; ++i) {
            int u = edge[i].first;
            int v = edge[i].second;
            if (id[u].first == -1) {
                id[u] = make_pair(cnt, cnt + deg[u] - D[u] - 1);
                cnt += (deg[u] - D[u]);
            }
            if (id[v].first == -1) {
                id[v] = make_pair(cnt, cnt + deg[v] - D[v] - 1);
                cnt += (deg[v] - D[v]);
            }
            if (id[N + i].first == -1) {
                id[N + i] = make_pair(cnt, cnt + 1);
                cnt += 2;
            }
            int t = id[N + i].first;
            adj[t][t + 1] = adj[t + 1][t] = true;
            for (int j = id[u].first; j <= id[u].second; ++j)
                adj[t][j] = adj[j][t] = true;
            for (int j = id[v].first; j <= id[v].second; ++j)
                adj[t + 1][j] = adj[j][t + 1] = true;
        }
        int j, sum = 0;
        n = cnt - 1;
        flag = 1;
        memset(match, 0, sizeof(match));
        for (start = 1; start <= n; ++start)
            if (match[start] == 0) FindAugmentPath();
        for (int i = 1; i <= n; ++i) {
            if (!match[i]) {
                flag = 0;
                break;
            }
        }
        if (flag)
            puts("Yes");
        else
            puts("No");
    }
}

J

题意

对于指定的正整数 n n n,求 ∫ 0 1 ( x − x 2 ) n d x \int_{0}^{1} (x-x^2)^n dx 01(xx2)ndx

解法

无脑OEIS法:首先二项展开,然后化简得到 ∑ r = 0 n ( − 1 ) r ( n 2 ) n + r + 1 \sum\limits_{r=0}^{n} \frac{(-1)^r{n\choose 2}}{n+r+1} r=0nn+r+1(1)r(2n),然后随便算丢OEIS。
正解:分部积分。

代码
#include <stdio.h>

#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <map>
#include <set>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const ll maxn = 2e6 + 111;
const ll mod=998244353;
ll pre[maxn];
ll qm(ll a,ll b){
    ll ret=1,cnt=a;
    while(b){
        if(b&1){
            ret=ret*cnt%mod;
        }
        cnt=cnt*cnt%mod;
        b>>=1;
    }
    return ret;
}
int main() {
    pre[1]=1;
    for(ll i=2;i<=2000010;++i){
        pre[i]=pre[i-1]*i%mod;
    }
    ll n;
    while(~scanf("%lld",&n)){
        printf("%lld\n",(pre[n]*pre[n]%mod*qm(pre[2*n+1],mod-2))%mod);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值