7.12 2020牛客暑期多校训练营(第一场)题解及补题
目录
7.12 2020牛客暑期多校训练营(第一场)题解及补题
比赛过程
最先开的F题,一开始我们想的是重复长度到两个字符串的最小公倍数,但是忽略了时间复杂度T了一发,幸好没一会就想到了正解。然后就去看J题了,J题找规律找了很久。
题解
F Infinite String Comparision
题意
给你两个字符串,比较这两个字符串各自重复无限次以后的字典序。
解法
将两个字符串重复到这两个字符串长度中较大的那个的两倍的长度,就可以比较了。在重复过程中若字符不相等可直接比较出答案。
代码
#include <bits/stdc++.h>
#define IO ios::sync_with_stdio(0), cin.tie(0)
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
const int inf = ~0u >> 1;
typedef pair<int,int> P;
#define REP(i, a, n) for (int i = a; i < (n); ++i)
#define PER(i, a, n) for (int i = (n) - 1; i >= a; --i)
int main() {
IO;
string a, b;
while (cin >> a >> b) {
if (a == b) {
cout << "=" << endl;
}
else {
string aa, bb;
int la = a.length(), lb = b.length();
int cnt = max(la,lb)*2;
bool flag=true;
REP(i, 0, cnt) {
if(a[i % la]>b[i % lb]) {
cout << ">" << endl;
flag=false;
break;
}
else if(a[i % la]<b[i % lb]) {
cout << "<" << endl;
flag=false;
break;
}
}
if (flag==true)
{
cout << "=" << endl;
}
}
}
return 0;
}
J Easy Integration
题意
给你n,\(\int_0^1(x - x^2)^n = \frac {p}{q}\),输出 \((p * q^{-1})\ mod\ 998244353\)
解法
找规律,答案就是\(\frac{(n!)^2}{(2n + 1)!}\),再利费马小定理求逆元即可
代码
#include <bits/stdc++.h>
#define IO ios::sync_with_stdio(0), cin.tie(0)
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
const int inf = ~0u >> 1;
typedef pair<int,int> P;
#define REP(i, a, n) for (int i = a; i < (n); ++i)
#define PER(i, a, n) for (int i = (n) - 1; i >= a; --i)
const ll mod = 998244353;
ll b[2 * maxn], c[maxn];
ll qpowmod(ll a, ll b, ll p){
a %= p;
ll ans = 1,base = a;
while(b != 0){
if(b & 1)
ans = ans * base % p;
base = base * base % p;
b >>= 1;
}
return ans;
}
ll getinv(ll a) {
return qpowmod(a, mod - 2, mod);
}
int main() {
IO;
ll n;
b[0] = 1;
REP(i, 1, 2 * maxn + 1) {
b[i] = b[i - 1] * i % mod;
}
//cout << b[2 * (ll)1e6 + 1] << endl;
while (cin >> n) {
ll fenmu = (b[2 * n + 1] % mod * getinv(qpowmod(b[n], 2, mod))) % mod;
//cout << fenmu << endl;
ll ans = (1ll * getinv(fenmu)) % mod;
cout << ans << endl;
}
return 0;
}
I 1 or 2
题意
给你n个点m条边组成的图,问你能不能选择其中的一些边使得点\(\,a_i\,\)的度等于\(\,d_i\,\)
解法
分两种情况:
- 因为选择一条边的话将会有两个点的度+1,所以如果\(\,\sum d_i\,\)等于奇数则直接输出No
- 建立一个源点和一个汇点,对每个点\(\,a_i\,\)建立一条从源点到\(\,a_i\,\)的边,权值为\(\,d_i\,\),再将点\(\,a_i\,\)复制,建立一条从复制的\(\,a_i'\,\)到汇点的边,权值也为\(\,d_i\,\)。对于相连的两个点\(\,a_i\,\),\(\,a_j\,\),建立一条从\(\,a_i\,\)到\(\,a_j'\,\)的权值为1的边,再建立一条从\(\,a_j\,\)到\(\,a_i'\,\)的权值为1的边。然后跑一遍最大流,如果最大流等于\(\,\sum d_i\,\)则输出Yes,否则No
代码
#include <bits/stdc++.h>
#define IO ios::sync_with_stdio(0), cin.tie(0)
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
const int inf = ~0u >> 1;
typedef pair<int, int> P;
#define REP(i, a, n) for (int i = a; i < (n); ++i)
#define PER(i, a, n) for (int i = (n)-1; i >= a; --i)
struct E {
int from, to, cap, flow;
E() {}
E(int f, int t, int c, int fl) : from(f), to(t), cap(c), flow(fl) {}
};
struct Dinic {
int n, m, s, t;
vector<E> e;
vector<int> G[maxn];
bool vis[maxn];
int cur[maxn];
int d[maxn];
const int INF = 1 << 29;
void init(int n, int s, int t) {
this->n = n, this->s = s, this->t = t;
e.clear();
for (int i = 0; i < n; ++i) G[i].clear();
}
void add(int from, int to, int cap) {
e.push_back(E(from, to, cap, 0));
e.push_back(E(to, from, 0, 0));
m = e.size();
G[from].push_back(m - 2);
G[to].push_back(m - 1);
}
bool bfs() {
queue<int> Q;
memset(vis, 0, sizeof(vis));
vis[s] = true;
d[s] = 0;
Q.push(s);
while (!Q.empty()) {
int x = Q.front();
Q.pop();
for (int i = 0; i < G[x].size(); ++i) {
E &ed = e[G[x][i]];
if (!vis[ed.to] && ed.cap > ed.flow) {
vis[ed.to] = true;
d[ed.to] = d[x] + 1;
Q.push(ed.to);
}
}
}
return vis[t];
}
int dfs(int x, int a) {
if (x == t || a == 0) return a;
int flow = 0, f;
for (int &i = cur[x]; i < G[x].size(); ++i) {
E &ed = e[G[x][i]];
if (d[ed.to] == d[x] + 1 &&
(f = dfs(ed.to, min(a, ed.cap - ed.flow))) > 0) {
ed.flow += f;
e[G[x][i] ^ 1].flow -= f;
flow += f;
a -= f;
if (a == 0) break;
}
}
return flow;
}
int max_flow() {
int ans = 0;
while (bfs()) {
memset(cur, 0, sizeof(cur));
ans += dfs(s, INF);
}
return ans;
}
} DC;
int d[55];
int main() {
IO;
int n, m;
while (cin >> n >> m) {
DC.init(2 * n + 2, 0, n + n + 1);
int sum = 0;
REP(i, 1, n + 1) {
cin >> d[i];
DC.add(0, i, d[i]);
DC.add(n + i, n + n + 1, d[i]);
sum += d[i];
}
if (sum % 2) {
cout << "No" << endl;
continue;
}
while (m--) {
int a, b;
cin >> a >> b;
DC.add(a, b + n, 1);
DC.add(b, a + n, 1);
}
if (DC.max_flow() == sum) {
cout << "Yes" << endl;
}
else
cout << "No" << endl;
}
return 0;
}
H Minimum-cost Flow
题意
给定一个流网络 n≤50,m≤100,∑m≤104(n个点,m条边),q(∑q≤106) 次询问,每次给定一个分数 u/v,问所有边的容量是 u/v 时费用流的费用。
解法
首先由于边的流量均为分数(u/v),而总流量为 11 个单位。我们先扩大v/u倍,将每条边的流量固定为 1 个单位,此时流量为 v/u 个单位。在最大流使用 SPFA 查找路径时,当查找到一条路径时,此路径的流量一定为 1 (根据设定)。由于采用的本身便是最短路查找路径,得到的路径的费用为当前网络图下的最低费用。可以稍微修改费用流板子中的一部分代码,使得每次得到路径并结算费用时,将每次得到的路径费用记录下来并保存。
代码
#include <algorithm>
#include <bitset>
#include <cctype>
#include <cerrno>
#include <clocale>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <limits>
#include <list>
#include <map>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <utility>
#include <vector>
#include <cwchar>
#include <cwctype>
#include<unordered_map>
using namespace std;
#define ls rt << 1
#define rs ls | 1
#define Min(x, y) x = min(x, y)
#define Max(x, y) x = max(x, y)
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define pir pair <int, int>
#define MK(x, y) make_pair(x, y)
#define MEM(x, b) memset(x, b, sizeof(x))
#define MPY(x, b) memcpy(x, b, sizeof(x))
#define lowbit(x) ((x) & -(x))
#define P2(x) ((x) * (x))
typedef long long ll;
const int Mod = 1e9 + 7;
const int N = 55;
const int M = 210;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
struct Edge
{
ll f, w;
int v, nxt;
}G[M];
ll dis[N], flow[N];
ll mxflow, mincost;
int n, m, sp, tp;
int h[N], cnt, L, R;
int pre[N], lst[N];
bool vis[N];
map <ll, ll> mp;
ll gcd(ll a, ll b) {
return !b ? a : gcd(b, a % b);
}
void init() {
for (int i = 1; i <= n; i++)
h[i] = -1;
L = 1, R = n, cnt = 0;
mxflow = mincost = 0;
mp.clear();
}
void Add(int u, int v, ll f, ll w) {
G[cnt].v = v, G[cnt].f = f, G[cnt].w = w;
G[cnt].nxt = h[u], h[u] = cnt++;
G[cnt].v = u, G[cnt].f = 0, G[cnt].w = -w;
G[cnt].nxt = h[v], h[v] = cnt++;
}
bool Spfa() { // 找到一条最短路的增广
queue <int> q;
for (int i = L; i <= R; i++) {
dis[i] = flow[i] = LINF;
vis[i] = false;
}
q.push(sp);
vis[sp] = true;
dis[sp] = 0, pre[tp] = -1;
while (!q.empty()) {
int u = q.front();
q.pop();
vis[u] = false;
for (int i = h[u]; i != -1; i = G[i].nxt) {
ll f = G[i].f, w = G[i].w;
int v = G[i].v;
if (f && dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
pre[v] = u;
lst[v] = i;
flow[v] = min(flow[u], f);
if (!vis[v]) {
vis[v] = true;
q.push(v);
}
}
}
}
return pre[tp] != -1;
}
void MCFC() { // 对于当前最短路更新流量
while (Spfa()) {
int now = tp;
mxflow += flow[tp];
mincost += flow[tp] * dis[tp];
mp[flow[tp] * dis[tp]] += flow[tp];
while (now != sp) {
G[lst[now]].f -= flow[tp];
G[lst[now] ^ 1].f += flow[tp];
now = pre[now];
}
}
}
int main()
{
while (~scanf("%d %d", &n, &m)) {
sp = 1, tp = n;
init();
for (int i = 0; i < m; i++) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
Add(u, v, 1, w);
}
MCFC();
int q;
scanf("%d", &q);
while (q--) {
ll u, v;
scanf("%lld %lld", &u, &v);
ll x = 0, y = v;
if (mxflow * u < v)
puts("NaN");
else {
ll x = 0, y = v;
// 从小到大删
for (auto it : mp) {
// 加u
if (it.second * u < v) {
v -= it.second * u;
x += it.first / it.second * u;
}
// 加剩余的v
else {
x += it.first / it.second * v;
break;
}
}
ll gc = gcd(x, y);
printf("%lld/%lld\n", x / gc, y / gc);
}
}
}
return 0;
}

浙公网安备 33010602011771号