AtCoder Beginner Contest 247[题解D~G]

\(ABC247\)

\(D\)

题目大意

你有一个队列,你需要完成 \(q\) 个操作,每种操作有如下两种形式:

  • \(1\) \(x\) \(c\):在队尾插入 \(c\) 个球,每个球上的数字为 \(x\)

  • \(2\) \(c\):取出队首 \(c\) 个球,并输出这 \(c\) 个球上数字的和。

\(1\leq q\leq 2\times 10^5,0\leq x\leq 10^9,1\leq c\leq 10^9\)

保证取出的球不会超过现存的球。

\(Sol\)

用结构体包装每个插入,取出的时候注意一下边界等条件即可。

\(code\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10;
inline int read()
{
	int s = 0, w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') { if(ch == '-') w *= -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * w;
}
struct node{
	int x, c;
}sck[N];
int q, redc, redx;
int st, ed, sum[N], cnt[N];
signed main()
{
	q = read();
	st = 1, ed = 0;
	for(register int i = 1; i <= q; i++){
		int opt = read();
		if(opt == 1){
			int x = read(), c = read();
			sck[++ed] = (node){x, c}, sum[ed] = sum[ed - 1] + c, cnt[ed] = cnt[ed - 1] + x * c;
		}
		else{
			int c = read();
			int l = st, r = ed, res = st - 1;
			while(l <= r){
				int mid = (l + r) >> 1;
				if(sum[mid] - sum[st - 1] - redx <= c) res = mid, l = mid + 1;
				else r = mid - 1; 
			}
			int ans = cnt[res] - cnt[st - 1] - redc;
			//cout << "res:" << res << "\n";
		//	cout << redc << " " << redx << "\n";
			c = c - (sum[res] - sum[st - 1] - redx), st = res + 1;
			//cout << c << "\n";
			ans = ans + c * sck[st].x;
			redc = c * sck[st].x, redx = c;
			printf("%lld\n", ans);
		}
	}
	return 0;
}

\(E\)

题目大意

给定一组长为 \(n\) 的序列 \(A\),以及两个整数 \(x\)\(y\),询问有多少个区间满足区间最大值为 \(x\),最小值为 \(y\)

\(1\leq n\leq 2 \times 10^5,1\leq A_i\leq 2\times 10 ^5,1\leq Y\leq X\leq 2\times 10 ^5\)

\(Sol\)

枚举左端点,线段树二分或者 \(ST\) 表都可以快速确定符合条件的区间范围。

\(code\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10, INF = 1e18;
inline int read()
{
	int s = 0, w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') { if(ch == '-') w *= -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * w;
}
struct Tree{
	int mx, mi;
}tr[4 * N];
int n, x, y, ans;
int arr[N];
inline void build(int k, int l, int r)
{
	if(l == r) { tr[k].mx = tr[k].mi = arr[l]; return; }
	int mid = (l + r) >> 1;
	build(k << 1, l, mid), build(k << 1 | 1, mid + 1, r);
	tr[k].mx = max(tr[k << 1].mx, tr[k << 1 | 1].mx);
	tr[k].mi = min(tr[k << 1].mi, tr[k << 1 | 1].mi);
}
inline int askmx(int k, int l, int r, int x, int y)
{
	if(r < x || l > y) return 0;
	if(l >= x && r <= y) return tr[k].mx;
	int mid = (l + r) >> 1;
	return max(askmx(k << 1, l, mid, x, y), askmx(k << 1 | 1, mid + 1, r, x, y));
}
inline int askmi(int k, int l, int r, int x, int y)
{
	if(r < x || l > y) return INF;
	if(l >= x && r <= y) return tr[k].mi;
	int mid = (l + r) >> 1;
	return min(askmi(k << 1, l, mid, x, y), askmi(k << 1 | 1, mid + 1, r, x, y));
}
signed main()
{
	n = read(), x = read(), y = read();
	for(register int i = 1; i <= n; i++) arr[i] = read();
	build(1, 1, n);
	for(register int i = 1; i <= n; i++){ //枚举做端点 
//		cout << "start:" << i << "\n";
		int l = i, r = n, mxl = n + 1, mxr = 0, mil = n + 1, mir = 0;
		while(l <= r){
			int mid = (l + r) >> 1, res = askmx(1, 1, n, i, mid);
			if(res == x) mxl = mid, r = mid - 1;
			if(res < x) l = mid + 1;
			if(res > x) r = mid - 1;
		}
		l = i, r = n;
		while(l <= r){
			int mid = (l + r) >> 1, res = askmx(1, 1, n, i, mid);
			if(res == x) mxr = mid, l = mid + 1;
			if(res < x) l = mid + 1;
			if(res > x) r = mid - 1;
		}
		l = i, r = n;
		while(l <= r){
			int mid = (l + r) >> 1, res = askmi(1, 1, n, i, mid);
			if(res == y) mir = mid, l = mid + 1;
			if(res > y) l = mid + 1;
			if(res < y) r = mid - 1;
		}
		l = i, r = n;
		while(l <= r){
			int mid = (l + r) >> 1, res = askmi(1, 1, n, i, mid);
			if(res == y) mil = mid, r = mid - 1;
			if(res > y) l = mid + 1;
			if(res < y) r = mid - 1;
		}
//		cout << mxl << " " << mxr << "\n";
//		cout << mil << " " << mir << "\n";
		int L = max(mil, mxl), R = min(mir, mxr);
		if(L > R) continue;
		ans = ans + R - L + 1; 
	}
	cout << ans << "\n";
	return 0;
}

\(F\)

题目大意

你有 \(n\) 张卡牌,第 \(i\) 张牌正面写有一个数字 \(P_i\),背面写有另一个数字 \(Q_i\)

且数组 \(P\)\(Q\) 均为 \(1\)\(n\) 的排列。

抽出若干张牌,要求所有牌正面和反面数字覆盖 \(1\)\(n\)

\(1\leq n\leq 2 \times 10^5,1\leq P_i,Q_i\leq n\)

\(Sol\)

将正反面连边,显然原题意可转化为边覆盖。

注意到每个点的度数必然为 \(2\),则建出的图必然是若干个圈,这是显然的。

对于每个圈,可以通过 \(dp\) 计算出边覆盖的方案数,最后的答案是每个圈的方案数的乘积。

\(code\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10, mod = 998244353;
inline int read()
{
	int s = 0, w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') { if(ch == '-') w *= -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * w;
}
struct edge{ int to, id; };
int n, tot, cnt, ans;
int P[N], Q[N];
int dp[N][2]; //dp[i][1/0] 表示第 i 条边选或不选的方案数 
bool vis[N], Judge[2 * N];
vector<edge> G[N];
inline void DFS(int u)
{
	vis[u] = true;
	for(register edge v : G[u]){
		if(Judge[v.id]) continue;
		cnt++, Judge[v.id] = true;
		DFS(v.to);
	}
}
signed main()
{
	n = read();
	for(register int i = 1; i <= n; i++) P[i] = read();	
	for(register int i = 1; i <= n; i++) Q[i] = read();
	for(register int i = 1; i <= n; i++)
		G[P[i]].push_back((edge){Q[i], ++tot}), G[Q[i]].push_back((edge){P[i], tot});
	ans = 1;
	for(register int i = 1; i <= n; i++){
		if(!vis[i]){
			cnt = 0, DFS(i);
			if(cnt == 1) { ans = ans * cnt % mod; continue; }
			int res = 0; 
			//钦定第一条边不选
			dp[1][0] = 1, dp[1][1] = 0;
			for(register int i = 2; i < cnt; i++){ //最后一条边必选 
				dp[i][1] = (dp[i - 1][0] + dp[i - 1][1]) % mod;
				dp[i][0] = dp[i - 1][1]; 
			}
			res = (dp[cnt - 1][0] + dp[cnt - 1][1]) % mod;
			//钦定第一条边选 
			dp[1][0] = 0, dp[1][1] = 1;
			for(register int i = 2; i <= cnt; i++){ //最后一条边选或不选都可以 
				dp[i][1] = (dp[i - 1][0] + dp[i - 1][1]) % mod;
				dp[i][0] = dp[i - 1][1];
			}
			res = (res + dp[cnt][0] + dp[cnt][1]) % mod;
			ans = ans * res % mod;
		}
	}
	printf("%lld\n", ans);
	return 0;
}

\(G\)

题目大意

\(n\) 个人,第 \(i\) 个人属于 \(A_i\) 大学,擅长 \(B_i\) 专业,能力值为 \(C_i\)

定义一个合法的战队满足一下两个条件:

  • 战队里的任意两个人不属于同一个大学。

  • 战队里的任意两个人不擅长同一个专业。

设一个合法战队的最大人数为 \(K\),对于所有人数小于等于 \(K\) 的战队,计算出当前人数合法战队的最大能力值。

\(1\leq n\leq 3\times 10^4,1\leq A_i,B_i\leq 150,1\leq C_i\leq 10^9\)

\(Sol\)

最大费用流模板题,需要注意的是我们怎么计算从 \(1\)\(K\) 的答案。

如果我们每次都重新建边显然会超时,考虑建立一个二分汇点,每次超级汇点向二号汇点连一条限流 \(1\) 的边,就避免了清空等问题,网络流是一个会自己调整的过程,所以正确性显然。

\(code\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e4 + 10, M = 2e5, INF = 1e18;
inline int read()
{
	int s = 0, w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') { if(ch == '-') w *= -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * w;
}
struct Person{ int a, b, c; }P[N];
int n, pt, ca, cb, s, t, maxf, minc;
int cnt, ans[N];
int dis[N], Min[N], pre[N];
int tot = -1, v[2 * M], w[2 * M], c[2 * M], nex[2 * M], first[N];
bool vis[N];
map<int, int> ma, mb;
inline void Add(int x, int y, int z, int f)
{
	nex[++tot] = first[x];
	first[x] = tot, v[tot] = y, w[tot] = z, c[tot] = f;
}
inline bool SPFA()
{
	for(register int i = 1; i <= pt; i++) dis[i] = INF, vis[i] = false;
	queue<int> q;
	q.push(s), vis[s] = true, dis[s] = 0, Min[s] = INF;
	while(!q.empty()){
		int x = q.front(); q.pop();
		vis[x] = false;
		for(register int i = first[x]; i != -1 ; i = nex[i]){
			int to = v[i];
			if(!w[i]) continue;
			if(dis[to] > dis[x] + c[i]){
				dis[to] = dis[x] + c[i];
				Min[to] = min(Min[x], w[i]);
				pre[to] = i;
				if(!vis[to]) q.push(to), vis[to] = true; 
			}
		}
	}
	return dis[t] != INF;
}
inline void dinic()
{
	while(SPFA()){
		maxf += Min[t], minc += dis[t] * Min[t];
		int tem = t, i;
		while(tem != s){
			i = pre[tem];
			w[i] -= Min[t], w[i ^ 1] += Min[t];
			tem = v[i ^ 1];
		}
	}
}
signed main()
{
	memset(first, -1, sizeof(first));
	n = read();
	for(register int i = 1; i <= n; i++){
		P[i].a = read(), P[i].b = read(), P[i].c = read();
		if(!ma[P[i].a]) ma[P[i].a] = ++ca;
		if(!mb[P[i].b]) mb[P[i].b] = ++cb;
	}
	pt = n + ca + cb + 3, s = n + ca + cb + 1, t = s + 1;
	for(register int i = 1; i <= n; i++){ //建边
		Add(ma[P[i].a], i + ca, 1, -P[i].c), Add(i + ca, ma[P[i].a], 0, P[i].c);
		Add(i + ca, ca + n + mb[P[i].b], 1, 0), Add(ca + n + mb[P[i].b], i + ca, 0, 0);
	}
	for(register int i = 1; i <= ca; i++) Add(s, i, 1, 0), Add(i, s, 0, 0);
	for(register int i = 1; i <= cb; i++) Add(i + ca + n, t + 1, 1, 0), Add(t + 1, i + ca + n, 0, 0);
	for(register int i = 1, lst = 0; ; i++, lst = maxf){
		Add(t + 1, t, 1, 0), Add(t, t + 1, 0, 0);
		dinic();
		if(maxf == lst) break;
		ans[i] = -minc, lst = maxf;
	}
	printf("%lld\n", maxf);
	for(register int i = 1; i <= maxf; i++){
		printf("%lld\n", ans[i]);
	}
	return 0;
}
posted @ 2022-04-11 21:04  ╰⋛⋋⊱๑落叶๑⊰⋌⋚╯  阅读(81)  评论(0编辑  收藏  举报