牛客多校
A C : 2 , r a n k 517 AC : 2, rank517 AC:2,rank517
7.12 7.12 7.12
题单
- F ( 1271 / 5027 ) F(1271/5027) F(1271/5027) AC 【模拟(签到)】
- H ( 140 / 1428 ) H(140/1428) H(140/1428) 补 【费用流】
- I ( 187 / 2699 ) I(187/2699) I(187/2699) 补 【开花 & 奇妙建图】
- J ( 906 / 2933 ) J(906/2933) J(906/2933) AC 【数学】
F. Infinite String Comparision
题意:
给两个串 a a a, b b b 问无限个 a a a 拼接后与无限个 b b b 拼接后哪个串的字典序大
total length of input strings ≤ \le ≤ 2 e 6 2e6 2e6
思路:
遍历 m a x ( ∣ a ∣ , ∣ b ∣ ) ∗ 2 max(|a|,|b|)*2 max(∣a∣,∣b∣)∗2 长度的串
#include<bits/stdc++.h>
using namespace std;
int main(){
string a,b;
ios::sync_with_stdio(0);cin.tie(0);
while(cin >> a >> b){
int f = 0;
int n = a.size();
int m = b.size();
for(int i = 0;i < 2*(max(n,m));i++){
if(a[i%n]<b[i%m]){
f = 1;//<
break;
}
if(a[i%n]>b[i%m]){
f = -1;
break;
}
}
if(f == 1){
cout << "<" << endl;
}
else if(f == -1){
cout << ">" << endl;
}
else{
cout << "=" << endl;
}
}
}
J. Easy Integration
题意:
∫
0
1
(
x
−
x
2
)
n
d
x
\int _{0}^{1}\left( x-x^{2}\right) ^{n}dx
∫01(x−x2)ndx
对 998244353
取模 (安利一个网站 手写数学公式转LaTex)
思路
OEIS , (不是 ::aru:cheer::
可以用 分部积分
∫
u
d
v
=
u
v
−
∫
v
d
u
\int udv = uv-\int vdu
∫udv=uv−∫vdu
第一次分部积分得到
n
n
+
1
∫
0
1
x
n
+
1
(
1
−
x
)
n
−
1
d
x
\frac n {n+1} \int_0^1x^{n+1}(1-x)^{n-1}dx
n+1n∫01xn+1(1−x)n−1dx
第二次分部积分得到
n
(
n
−
1
)
(
n
+
1
)
(
n
+
2
)
∫
0
1
x
n
+
2
(
1
−
x
)
n
−
2
d
x
\frac {n(n-1)} {(n+1)(n+2)} \int_0^1x^{n+2}(1-x)^{n-2}dx
(n+1)(n+2)n(n−1)∫01xn+2(1−x)n−2dx
n
n
n 次后得到
n
(
n
−
1
)
.
.
.
1
(
n
+
1
)
(
n
+
2
)
.
.
.
(
n
+
n
)
∫
0
1
x
n
+
n
d
x
=
n
!
⋅
n
!
(
2
n
)
!
⋅
1
2
n
+
1
=
(
n
!
)
2
(
2
n
+
1
)
!
\frac {n(n-1)...1} {(n+1)(n+2)...(n+n)} \int_0^1x^{n+n}dx = \\\frac {n!\cdot n!} {(2n)!} \cdot \frac 1 {2n+1} = \\\dfrac{\left( n!\right) ^{2}}{\left( 2n+1\right)! }
(n+1)(n+2)...(n+n)n(n−1)...1∫01xn+ndx=(2n)!n!⋅n!⋅2n+11=(2n+1)!(n!)2
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 2e6 + 10;
const int mod = 998244353;
int F[maxn];
void init() {
F[0] = 1;
for (int i = 1; i < maxn; i++) {
F[i] = 1ll * F[i - 1] * i % mod;
}
}
int qpow(int a, int p) {
int ans = 1;
while (p) {
if (p & 1) ans = ans * a % mod;
a = a * a % mod;
p >>= 1;
}
return ans;
}
signed main() {
init();
int n;
while (cin >> n) {
int ans = F[n] * F[n] % mod * qpow(F[2 * n + 1], mod - 2) % mod;
cout << ans << endl;
}
}
I. 1 or 2
题意:
给一个图,给定点的度数,问能不能通过删边满足题意。
d i ∈ [ 1 , 2 ] d_i \in [1,2] di∈[1,2]
思路:
奇妙建图,参考 Hdu 3551
跑一边开花模板
#include<bits/stdc++.h>
using namespace std;
const int maxn = 550;
const int maxm = 1000;
struct Edge {
int to, nxt;
}E[maxm];
int head[maxn];
int tot;
void addEdge(int from, int to, bool istwo = false) {
E[tot] = Edge{ to,head[from] };
head[from] = tot++;
//双向边
if (istwo) {
E[tot] = Edge{ from,head[to] };
head[to] = tot++;
}
}
int fa[maxn];//并查集
int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void merge(int a, int b) {
a = find(a);
b = find(b);
if (a != b) fa[a] = b;
}
int n, m;
int match[maxn];//记录匹配
int Q[maxn], rear;//队列
int nxt[maxn], mark[maxn], vis[maxn];
int LCA(int x, int y) {
static int t = 0; t++;
while (1) {
if (x != -1) {
x = find(x); //点对应到花
if (vis[x] == t)return x;
vis[x] = t;
if (match[x] != -1) x = nxt[match[x]]; // 有匹配,向上走
else x = -1;//停下来
}
swap(x, y);
}
}
void group(int a, int p) {
while (a != p) {
int b = match[a], c = nxt[b];
if (find(c) != p) nxt[c] = b;
if (mark[b] == 2) mark[Q[rear++] = b] = 1;
if (mark[c] == 2) mark[Q[rear++] = c] = 1;
merge(a, b); merge(b, c);
a = c;
}
}
void aug(int s) {
for (int i = 1; i <= n; i++) {
nxt[i] = -1; fa[i] = i; mark[i] = 0; vis[i] = -1;
}
mark[s] = 1;
Q[0] = s; rear = 1;
for (int front = 0; match[s] == -1 && front < rear; front++) {
int x = Q[front];
for (int i = head[x]; i != -1; i = E[i].nxt) {
int y = E[i].to;
if (match[x] == y) continue; // x与y已匹配,忽略
if (find(x) == find(y)) continue; // x与y同在一朵花,忽略
if (mark[y] == 2) continue; // 偶环,忽略
if (mark[y] == 1) { // 缩点
int r = LCA(x, y); //
if (find(x) != r) nxt[x] = y;
if (find(y) != r) nxt[y] = x;
group(x, r);
group(y, r);
}
else if (match[y] == -1) { // y自由,可以增广
nxt[y] = x;
for (int u = y; u != -1; ) { // 交叉链取反
int v = nxt[u];
int mv = match[v];
match[v] = u, match[u] = v;
u = mv;
}
break; // 搜索成功,退出循环将进入下一阶段
}
else {
nxt[y] = x;
mark[Q[rear++] = match[y]] = 1;
mark[y] = 2;
}
}
}
}
int d[maxn];
int p[maxn];
void init() {
memset(head, -1, sizeof head);
memset(p, 0, sizeof p);
memset(d, 0, sizeof d);
tot = 0;
}
int main() {
while (~scanf("%d%d", &n, &m)) {
init();
int x = n;
for (int i = 1; i <= x; i++) {
scanf("%d", d + i);
if (d[i] == 2) p[i] = ++n;
}
x = n;
for (int i = 0; i < m; i++) {
int f, t;
scanf("%d%d", &f, &t);
int a = ++n;
int b = ++n;
addEdge(a, b, true);
addEdge(a, f, true);
addEdge(b, t, true);
if (p[f] != 0) {
addEdge(p[f], a, true);
}
if (p[t] != 0) {
addEdge(p[t], b, true);
}
}
for (int i = 1; i <= n; i++) {
match[i] = -1;
}
for (int i = 1; i <= n; i++) {
if (match[i] == -1) aug(i);
}
int tot = 0;
for (int i = 1; i <= n; i++) {
if (match[i] != -1)tot++;
}
printf("%s\n", tot == n ? "Yes" : "No");
}
}
H. Minimun-cost Flow
题意:
给一个图,给定边的单位花费,再接下来的 q q q 次查询中, 给出 u u u , v v v
使得所有边的最大流量为 u / v u/v u/v , 对于每一次询问,输出流量为 1 1 1 的最小花费
若没有答案则输出 NaN
思路:
若对于每一次询问,都跑一次MCMF, 就 t 了。
边容量 | 需求流量 | 费用 | 最大流 |
---|---|---|---|
u / v u/v u/v | 1 1 1 | w w w | m m m |
u u u | v v v | w ⋅ v w\cdot v w⋅v | m ⋅ v m \cdot v m⋅v |
1 1 1 | v / u v/u v/u | w v u \dfrac {wv} u uwv | m v u \dfrac {mv} u umv |
可以先跑一次边容量为1 的MCMF,记录每次增广的 d i s i dis_i disi
核心代码
while (q--) {
int u, v;
scanf("%lld%lld", &u, &v);
if (maxflow * u < v) {
printf("NaN\n");
continue;
}
int sum = v, ans = 0;
for (int i = 0; i < cnt; i++) {
if (sum >= u) sum -= u, ans += d[i] * u;
else { ans += sum * d[i]; break; }
}
int g = gcd(ans, v);
printf("%lld/%lld\n", ans / g, v / g);
}
完整代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 5e3 + 10;
int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
struct E {
int to, dis, flow, next; //dis 是费用
}e[maxn];
int tot, head[maxn];
void addEdge(int from, int to, int cap, int cost) {
e[tot] = E{ to, cost, cap,head[from] };
head[from] = tot++;
}
int vis[maxn], dis[maxn], incf[maxn], pre[maxn];
int s, t, n, m;
int maxflow, mincost;
int d[maxn], cnt;
bool spfa() {
queue<int>Q;
memset(dis, 0x3f, sizeof dis);
memset(vis, 0, sizeof vis);
Q.push(s);
dis[s] = 0;
vis[s] = 1;
incf[s] = 1 << 30;
while (!Q.empty()) {
int u = Q.front(); Q.pop();
vis[u] = 0;
for (int i = head[u]; i != -1; i = e[i].next) {
if (!e[i].flow) continue; //没有残余流量
int v = e[i].to;
if (dis[v] > dis[u] + e[i].dis) {
dis[v] = dis[u] + e[i].dis;
incf[v] = min(incf[u], e[i].flow);
pre[v] = i;
if (!vis[v]) vis[v] = 1, Q.push(v);
}
}
}
if (dis[t] == 0x3f3f3f3f3f3f3f3f)return 0;
return 1;
}
void MCMF() {
while (spfa()) {
int x = t;
maxflow += incf[t];
mincost += dis[t] * incf[t];
d[cnt++] = dis[t];
while (x != s) {
int p = pre[x];
e[p].flow -= incf[t];
e[p ^ 1].flow += incf[t];
x = e[p ^ 1].to;
}
}
}
signed main() {
while (~scanf("%lld%lld", &n, &m)) {
memset(head, -1, sizeof head);
for (int a, b, x, i = 1; i <= m; i++) {
scanf("%lld%lld%lld", &a, &b, &x);
addEdge(a, b, 1, x);
addEdge(b, a, 0, -x);
}
maxflow = mincost = 0;
s = 1, t = n;
cnt = 0;
MCMF();
int q;
scanf("%lld", &q);
while (q--) {
int u, v;
scanf("%lld%lld", &u, &v);
if (maxflow * u < v) {
printf("NaN\n");
continue;
}
int sum = v, ans = 0;
for (int i = 0; i < cnt; i++) {
if (sum >= u) sum -= u, ans += d[i] * u;
else { ans += sum * d[i]; break; }
}
int g = gcd(ans, v);
printf("%lld/%lld\n", ans / g, v / g);
}
}
}