【2020暑假牛客多校(一)】

牛客多校

Home

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(xx2)ndx
998244353 取模 (安利一个网站 手写数学公式转LaTex)

思路

OEIS , (不是 ::aru:cheer::

可以用 分部积分
∫ u d v = u v − ∫ v d u \int udv = uv-\int vdu udv=uvvdu
第一次分部积分得到
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+1n01xn+1(1x)n1dx
第二次分部积分得到
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(n1)01xn+2(1x)n2dx
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(n1)...101xn+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 wv m ⋅ v m \cdot v mv
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);
		}
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值