Chgtaxihe's blog Chgtaxihe's blog
首页
练习
算法学习
读书笔记
小玩意

Chgtaxihe

首页
练习
算法学习
读书笔记
小玩意
  • 训练补题
  • Codeforces 刷题计划
  • 概率dp练习
  • dp专题练习
  • 数论练习
  • 搜索专题练习
  • 图论练习
  • 基础字符串练习
  • AtCoder练习
  • 题解们
  • solutions

  • 训练补题

    • 训练补题-个人1
    • 训练补题-个人10
    • 训练补题-个人2
    • 训练补题-个人5
    • 训练补题-个人6
    • 训练补题-个人8
    • 训练补题-个人9
    • 训练补题-牛客训练赛1
      • J: Easy Integration 分部积分
      • H: Minimum-cost Flow 最小费用流 思维
      • F: Infinite String Comparision
      • I: 1 or 2 一般图最大匹配(带花树)
    • 训练补题-牛客训练赛10
    • 训练补题-牛客训练赛2
    • 训练补题-牛客训练赛3
    • 训练补题-牛客训练赛6
    • 训练补题-牛客训练赛6
    • 训练补题-牛客训练赛7
    • 训练补题-牛客训练赛8
    • 训练补题-牛客训练赛9
    • 训练补题-杭电多校10
    • 训练补题-杭电多校2
    • 训练补题-杭电多校3
    • 训练补题-杭电多校6
    • 训练补题-杭电多校9
    • NWERC 2018-训练补题11
    • SpbKOSHP 19-训练补题12
    • Gym102576-训练补题13
    • GCPC2018-训练补题14
    • Gym102202-训练补题15

训练补题-牛客训练赛1

# 牛客训练赛1补题记录

牛客给的题解真的敷衍

# J: Easy Integration 分部积分

题意:对于1≤n≤1061\le n \le 10^61≤n≤106,求∫01(x−x2)ndx\int_{0}^{1}(x-x^2)^n \mathrm{d}x∫01​(x−x2)ndx

2种方法:

  1. 分部积分法

    ∫01(x−x2)ndx=∫01xn(1−x)ndx=1n+1∫01(1−x)ndxn+1分部积分=1n+1[0+n∫01xn+1(1−x)n−1]dx继续分部积分=n(n−1)(n+1)(n+2)∫01xn+2(1−x)n−2dx=多次分部积分⋯=n!(n+1)(n+2)⋯(n+n)∫01x2n(1−x)0dx=(n!)2(2n+1)!\begin{aligned} \int_{0}^{1}(x-x^2)^n \mathrm{d}x &= \int_{0}^{1}x^n(1-x)^n \mathrm{d}x\\ &=\frac{1}{n+1} \int_{0}^{1}(1-x)^n \mathrm{d}x^{n+1} \quad \text{分部积分}\\ &=\frac{1}{n+1}[0+n\int_{0}^{1}x^{n+1}(1-x)^{n-1}] \mathrm{d}x \quad \text{继续分部积分}\\ &=\frac{n(n-1)}{(n+1)(n+2)} \int_{0}^{1} x^{n+2}(1-x)^{n-2} \mathrm{d}x\\ &=\text{多次分部积分} \cdots \\ &=\frac{n!}{(n+1)(n+2)\cdots(n+n)}\int_{0}^{1}x^{2n}(1-x)^0 \mathrm{d}x\\ &=\frac{(n!)^2}{(2n+1)!} \end{aligned} ∫01​(x−x2)ndx​=∫01​xn(1−x)ndx=n+11​∫01​(1−x)ndxn+1分部积分=n+11​[0+n∫01​xn+1(1−x)n−1]dx继续分部积分=(n+1)(n+2)n(n−1)​∫01​xn+2(1−x)n−2dx=多次分部积分⋯=(n+1)(n+2)⋯(n+n)n!​∫01​x2n(1−x)0dx=(2n+1)!(n!)2​​

  2. 公式

    贝塔函数(第一类欧拉积分):

    B(x,y)=∫01tx−1(1−t)y−1dtB(x,y)=\int_{0}^{1} t^{x-1}(1-t)^{y-1}\mathrm{d}t B(x,y)=∫01​tx−1(1−t)y−1dt

    当x,y是正整数的时候,我们可以从伽马函数定义得到如下式子:

    B(x,y)=(x−1)!(y−1)!(x+y−1)!B(x,y)=\frac{(x-1)!(y-1)!}{(x+y-1)!} B(x,y)=(x+y−1)!(x−1)!(y−1)!​

AC代码

#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define int128 __int128_t
#define Android ios::sync_with_stdio(false), cin.tie(NULL)
#define redirect_input freopen("./input.txt", "r", stdin);
#define redirect_output freopen("./output.txt", "w", stdout);
#define debug(s, r) std::cerr << #s << ": " << (s) << (r == 0 ? ' ' : '\n')
#define pii pair<int, int>
#define sqr(x) ((x) * (x))
using namespace std;

const int maxn = 1e6 + 1000;
const ll mod = 998244353;

ll qpow(ll base, ll t){
    ll res = 1;
    while(t){
        if(t & 1) res = base * res % mod;
        base = base * base % mod;
        t >>= 1;
    }
    return res;
}

ll factor[maxn * 2], inv_fac[maxn * 2];

void init(){
    factor[1] = 1;
    for(int i=2; i < maxn * 2; i++){
        factor[i] = factor[i-1] * i % mod;
    }
    inv_fac[maxn * 2 - 1] = qpow(factor[maxn * 2 - 1], mod - 2);
    for(int i = maxn * 2 - 2; i > 1; i--){
        inv_fac[i] = inv_fac[i+1] * (i + 1) % mod;
    }
}

void solve() {
    init();
    ll n;
    while(cin >> n){
        cout << (sqr(factor[n]) % mod * inv_fac[n * 2 + 1]) % mod << '\n'; 
    }
}

signed main() {
    Android;
    solve();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

# H: Minimum-cost Flow 最小费用流 思维

题意:

给定一个nnn个点mmm条边的图,第iii条边的费用为cic_ici​。qqq次询问每次给出uuu和vvv,代表每条边的容量都为u/vu/vu/v,求流量为111时的最小费用。

做法:

由于询问次数过多,不可能每个询问都跑一遍最小费用流。

设c(x,y)c(x, y)c(x,y)代表网络中每条边的容量都为xxx,总流量为yyy时的最小费用,可以得到以下式子。

c(uv,1)=c(u,v)×1v=c(1,vu)×uvc(\frac{u}{v}, 1)=c(u,v)\times \frac{1}{v}=c(1, \frac{v}{u})\times \frac{u}{v} c(vu​,1)=c(u,v)×v1​=c(1,uv​)×vu​


考虑只跑一次最小费用流,得到c(1, \text{max_flow}),在跑最小费用流的过程中,我们把每次增广的费用记录下来(AC代码中的map<ll,ll> extension)。

在查询时,分情况讨论

  • 如果\text{max_flow}<\frac{v}{u},无解。
  • 否则,按费用从小到大遍历增广路。设该增广路的容量为capcapcap,费用为ccc,剩余未跑的流量为ru\frac{r}{u}ur​(初始时r=vr=vr=v):
    • 若cap<rucap<\frac{r}{u}cap<ur​,则该增广路被完全占用,则总费用costcostcost增加uv×cap×c\frac{u}{v}\times cap\times cvu​×cap×c,同时r′=r−cap×ur'=r-cap\times ur′=r−cap×u,继续遍历
    • 否则,该增广路并未完全占用,费用costcostcost增加uv×ru×c=rcv\frac{u}{v}\times\frac{r}{u}\times c=\frac{rc}{v}vu​×ur​×c=vrc​,同时r′=0r'=0r′=0,退出遍历

可见在这个过程中,分母永远是vvv,因此运算过程中可以忽略。

最后对分子分母化简即可。

AC代码:

#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define int128 __int128_t
#define Android ios::sync_with_stdio(false), cin.tie(NULL)
#define redirect_input freopen("./input.txt", "r", stdin);
#define redirect_output freopen("./output.txt", "w", stdout);
#define debug(s, r) std::cerr << #s << ": " << (s) << (r == 0 ? ' ' : '\n')
#define pii pair<int, int>
#define sqr(x) ((x) * (x))
using namespace std;

struct Dinic{
    const static ll inf = 0x7f7f7f7f7f7f7f7f;
    const static int mx_edge = 150, mx_node = 70;
    int head[mx_node], edge_cnt, n;
    int cur[mx_node], que[mx_node], q_head, q_tail;
    bool in_queue[mx_node], vis[mx_node], extend_success;
    ll dist[mx_node], mx_flow;

    map<ll, ll> extension; 

    struct E{
        int v, nxt;
        ll cap, cost;
    } edge[2 * mx_edge];

    void init(int nx){
        n = nx, edge_cnt = 0;
        for(int i=0; i<=n; i++) head[i] = -1;
        extension.clear();
    }

    void add_edge(int u, int v, ll cap, ll cost){
        edge[edge_cnt] = {v, head[u], cap, cost};
        head[u] = edge_cnt++;
        edge[edge_cnt] = {u, head[v], 0, -cost};
        head[v] = edge_cnt++;
    }

    bool spfa(int s, int t){
        for(int i=0; i<=n; i++) dist[i] = inf;
        q_head = q_tail = 0; que[q_tail++] = s;
        in_queue[s] = 1, dist[s] = 0;

        while(q_tail ^ q_head){
            int now = que[q_head++];
            q_head %= mx_node;
            in_queue[now] = false;
            for(int i=head[now], v; ~i; i=edge[i].nxt){
                if(edge[i].cap <= 0) continue;
                
                v = edge[i].v;
                if(dist[v] > dist[now] + edge[i].cost){
                    dist[v] = dist[now] + edge[i].cost;
                    if(!in_queue[v]){
                        in_queue[v] = true;
                        que[q_tail++] = v;
                        q_tail %= mx_node;
                    }
                }
            }
        }
        return dist[t] != inf;
    }

    ll dfs(int u, int t, ll lim){
        vis[u] = true;
        if(u == t){
            extend_success = true;
            mx_flow += lim;
            extension[dist[u]] += lim;
            return lim;
        }

        ll ret = 0, tmp;
        for(int & i=cur[u], v; ~i; i=edge[i].nxt){
            v = edge[i].v;
            if(dist[u] + edge[i].cost == dist[v] &&
               edge[i].cap > 0 &&
               (!vis[v] || v == t)){
                   tmp = dfs(v, t, min(lim-ret, edge[i].cap));
                   if(tmp > 0){
                       ret += tmp;
                       edge[i].cap -= tmp;
                       edge[i ^ 1].cap += tmp;
                       if(ret >= lim) break;
                   }
               }
        }

        return ret;
    }

    ll dinic(int s, int t){
        for(int i=0; i<=n; i++) in_queue[i] = false;
        mx_flow = 0;
        while(spfa(s, t)){
            for(int i=0; i<=n; i++) vis[i] = false;
            for(int i=0; i<=n; i++) cur[i] = head[i];
            do{
                extend_success = false;
                dfs(s, t, inf);
            }while(extend_success);
        }
        return mx_flow;
    }
}dinic;

void solve(int n, int m) {
    dinic.init(n);
    for(int i=0, u, v, c; i<m; i++){
        cin >> u >> v >> c;
        dinic.add_edge(u, v, 1, c);
    }
    ll mx_flow = dinic.dinic(1, n);

    int q; cin >> q;

    while(q--){
        ll u, v, r, numer = 0; // numer: numerator
        cin >> u >> v;
        if(mx_flow * u < v){
            cout << "NaN\n";
            continue;
        }

        r = v; // r: remaining
        for(pair<ll, ll> ext:dinic.extension){
            ll cost = ext.first, cap = ext.second;
            if(cap * u <= r){
                numer += cap * cost * u;
                r -= u * cap;
            }else{
                numer += r * cost;
                r = 0;
            }
            if(r <= 0) break;
        }
        ll g = __gcd(numer, v);
        cout << (numer / g) << "/" << (v / g) << '\n';
    }

}

signed main() {
    Android;
    int n, m;
    while(cin >> n >> m) solve(n, m);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150

# F: Infinite String Comparision

题意:给你两个串a,ba,ba,b,比较Sa=a∞S_a=a^{\infty}Sa​=a∞与Sb=b∞S_b=b^{\infty}Sb​=b∞的字典序大小。

思路很简单,但原理并不简单。

解法:比较a+ba+ba+b与b+ab+ab+a的字典序大小即可。

import sys
class IoTool:
    DEBUG = 0
    def _reader_dbg():
        with open('./input.txt', 'r') as f:
            lines = f.readlines()
        return iter(lines)
    reader = _reader_dbg() if DEBUG else iter(sys.stdin.read().split('\n'))

input = lambda :next(IoTool.reader)

info = list(IoTool.reader)
for i in range(len(info) // 2):
    a = info[i * 2].strip()
    b = info[i * 2 + 1].strip()
    apb = a + b
    bpa = b + a
    if apb < bpa:
        print('<')
    elif apb == bpa:
        print('=')
    else:
        print('>')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

原理与证明:

这题的重点在于判断两串是否相等。

根据Weak Periodicity Lemma,可以知道如果两个串的生成串Sa=Sb=SS_a=S_b=SSa​=Sb​=S,即a,ba,ba,b均为sss的循环节,那么gcd(∣a∣,∣b∣)gcd(\mid a\mid,\mid b\mid)gcd(∣a∣,∣b∣)也是SSS的周期。

那么可以将串a,ba,ba,b分别划分为多个块,每个块的长度为gcd(∣a∣,∣b∣)gcd(\mid a\mid,\mid b\mid)gcd(∣a∣,∣b∣)。设串aaa被分为xxx个块,串bbb被分为yyy个块。显然,gcd(x,y)=1gcd(x,y)=1gcd(x,y)=1。

现在问题变为:最少比较多少次,才能保证这x+yx+yx+y个块之间两两相同呢?显然次数的下界为x+y−1x+y-1x+y−1。

也就是说,对于串a,ba,ba,b的生成串,我们最少只需要比较 gcd(a,b)∗(x+y−1)=∣a∣+∣b∣−gcd(a,b)gcd(a,b) * (x+y-1)=\mid a\mid + \mid b\mid - gcd(a,b)gcd(a,b)∗(x+y−1)=∣a∣+∣b∣−gcd(a,b) 个字符即可确定两串的生成串是否相同。

设串aaa的生成串中,第iii个块为AiA_iAi​;BiB_iBi​同理。我们顺序判断 Ai=Bii∈{1,2,3,⋯,x+y−1}A_i=B_i\quad i\in\{1,2,3,\cdots,x+y-1\}Ai​=Bi​i∈{1,2,3,⋯,x+y−1},当Ai≠BiA_i\ne B_iAi​=Bi​时,判断对应块的字典序大小即可。由于x,yx,yx,y互质,该方法能够保证串a,ba,ba,b中的x+yx+yx+y个块两两相同(反证法可得)。

# I: 1 or 2 一般图最大匹配(带花树)

上次更新: 2021/02/24, 03:37:30
训练补题-个人9
训练补题-牛客训练赛10

← 训练补题-个人9 训练补题-牛客训练赛10→

最近更新
01
深入理解Java虚拟机
03-04
02
DNS工作原理
02-27
03
改用VuePress啦
02-23
更多文章>
Theme by Vdoing | Copyright © 2019-2021
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式