首页 > 其他分享 >UNIQUE VISION Programming Contest 2024 Spring(AtCoder Beginner Contest 346)

UNIQUE VISION Programming Contest 2024 Spring(AtCoder Beginner Contest 346)

时间:2024-03-25 21:47:16浏览次数:21  
标签:AtCoder return Beginner Contest int tree mid vis dp

C

我们用 \(1\sim K\) 的和减去出现在 \(1\sim K\) 中的数的和。

int n, k, a[N], res;
map<int, int> vis; 

signed main() {
	cin >> n >> k;
	_for(i, 1, n) cin >> a[i];
	res = k * (1 + k) / 2;
	_for(i, 1, n) if (a[i] >= 1 && a[i] <= k && !vis[a[i]]) res -= a[i], vis[a[i]] = 1;
	cout << res << endl;
}

D

这个题我脑溢血了,最开始从 \(2\) 维 dp 开始写,写了 10 min 发现假了,加了一维又调了好久好久。

\(dp_{i,j,k}\) 表示前 \(i\) 个数,存不存在有两个字符相邻 \(j=0/1\),当前这个位置翻转还是不翻转 \(k=0/1\)。

没有脑血栓真建议别看我代码,我写了 30 分钟。

int n, b[N], dp[N][2][2];
char a[N];

char get(char c, int x) {
	if (c == '0') {
		if (x == 1) return '1';
		return '0';
	}
	else {
		if (x == 1) return '0';
		return '1';
	}
}

signed main() {
	memset(dp, 0x3f, sizeof dp);
	cin >> n >> (a + 1);
	_for(i, 1, n) cin >> b[i];
	dp[1][0][0] = 0;
	dp[1][0][1] = b[1];
	_for(i, 2, n) {
		_for(j, 0, 1) {
			_for(k, 0, 1) {
				_for(w, 0, j) {
					int t = 0;
					if (a[i] == get(a[i - 1], k)) {
						if (j == w) dp[i][j][1] = min(dp[i][j][1], dp[i - 1][w][k] + b[i]);
						if (w == 0 && j == 1) dp[i][j][0] = min(dp[i][j][0], dp[i - 1][w][k]);
					}
					else {
						if (w == 0 && j == 1) dp[i][j][1] = min(dp[i][j][1], dp[i - 1][w][k] + b[i]);
						if (j == w) dp[i][j][0] = min(dp[i][j][0], dp[i - 1][w][k]);
					}
				}
			}
		}
	}
	cout << min(dp[n][1][0], dp[n][1][1]) << endl;
}

E

我们把操作倒着看。自己画一下就知道,倒着来看操作的话,每操作一列,其实就代表之后所有的操作都不会影响到这一列,等价于把这一列删了。

int n, m, c;
int t[N], a[N], x[N], vis[3][N], ans[N], res, cc;

signed main() {
	cin >> n >> m >> c;
	int tn = n, tm = m;
	_for(i, 1, c) cin >> t[i] >> a[i] >> x[i];
	_pfor(i, c, 1) {
		if (vis[t[i]][a[i]]) continue;
		vis[t[i]][a[i]] = 1;
		if (t[i] == 1) ans[x[i]] += m, n--; // n--代表删除这一行
		else ans[x[i]] += n, m--;
	}
	_for(i, 1, N - 5) res += ans[i], cc += (ans[i] > 0);
	if (res == tn * tm) cout << cc << endl;
	else {
		cout << cc + 1 << endl;
		cout << 0 << ' ' << tn * tm - res << endl;  
	}
	_for(i, 1, N - 5) {
		if (ans[i]) cout << i << ' ' << ans[i] << endl; 
	}
}

F

肯定考虑二分 \(x\)。这就意味着对于 \(T\) 的所有字符,都要去对应 \(f(S,N)\) 中的对应字符 \(x\) 次。我们不用这样考虑:如果当前 S 的指针匹配失败了,就把指针跳到开头再来匹配。我们直接把 \(S\) 复制 \(N\) 次,看成 \(len(S)\times N\) 长度的字符串就行。可以避免大量分讨。

还不明白?看代码吧。这样复杂度是 \(O(n\times \log^2(len(S)\times N))\)。非常卡常数。

有一个卡常技巧,把 sum 数组的小的那一维放前面去,直接快 \(0.5s\sim 1.5s\) 不等!我十分震撼!

#pragma GCC optimize(2)
#pragma GCC optimize(3)

#include <bits/stdc++.h>
using namespace std;

#define LL long long
#define PII pair<int, int>
#define _for(i, a, b) for (int i = (a); i <= (b); i++)
#define _pfor(i, a, b) for (int i = (a); i >= (b); i--)
//#define int long long
const int N = 1e5 + 5;

LL T; 
int n, m, sum[27][N];
// sum_{c,i} 表示S中前 $i$ 个字符c出现了多少次
char a[N], b[N];

inline LL get(LL x, int c) {
	return 1ll * sum[c][n] * (x / n) + sum[c][x % n]; // 代表复制后的S中前x个字符c出现了对少次
}

inline bool check(LL x) {
	if (x == 0) return 1;
	LL lst = 1;
	_for(i, 1, m) {
		int t = (int)(b[i] - 'a');
		LL l = lst, r = T * n;
		if (l > r) return 0;
		while (l < r) {
			LL mid = (l + r) >> 1;
			if (get(mid, t) - get(lst - 1, t) >= x) r = mid;
			else l = mid + 1;
		} 
		if (get(l, t) - get(lst - 1, t) < x) return 0;
		lst = l + 1;
	}
	return 1;
}

signed main() {
	scanf("%lld%s%s", &T, (a + 1), (b + 1));
	n = strlen(a + 1), m = strlen(b + 1);
	_for(i, 1, n) {
		int t = (int)(a[i] - 'a');
		_for(j, 0, 25) {
			if (t == j) sum[j][i] = sum[j][i - 1] + 1;
			else sum[j][i] = sum[j][i - 1];
		}
	}
	LL l = 0, r = T * n;
	while (l < r) {
		LL mid = (l + r + 1) >> 1;
		if (check(mid)) l = mid;
		else r = mid - 1;
	}
	if (!check(l)) puts("0");
	else cout << l << endl; 
}

G

赛时写主席树+树状数组真的是脑抽死了,关键是还是假算法。

我们考虑每一个数能在哪些区间中只出现一次。当前数字下标为 \(i\),往前看第一个和这个数字相同的下标为 \(l_i\),往后看第一个和这个数字相同的下标为 \(r_i\),那么答案就是 \((i-l_i)\times (r_i-i)\)。

但是会算重。所以我们看成 \(n\) 个左上角为 \((l_i,i)\) ,右下角 \((i,r_i)\) 的矩形,求这些矩形的面积并。

#include <bits/stdc++.h>
using namespace std;

#define ls p << 1
#define rs p << 1 | 1
#define PII pair<int, int>
#define _for(i, a, b) for (int i = (a); i <= (b); i++)
#define _pfor(i, a, b) for (int i = (a); i >= (b); i--)
#define int long long
const int N = 4e5 + 5;

int n, a[N], l[N], r[N], vis[N], b[N], n2, res;

struct edge {
	int x, l, r, val;
}ed[N];
bool cmp(edge x, edge y) {
	return x.x < y.x;
} 
int tot;

struct tt {
	int l, r, cnt, len; 
}tree[N * 4];

void push_up(int p) {
	if (tree[p].cnt) tree[p].len = b[tree[p].r + 1] - b[tree[p].l];
	else tree[p].len = tree[ls].len + tree[rs].len;
}

void build(int p, int l, int r) {
	tree[p].l = l, tree[p].r = r;
	if (l == r) return;
	int mid = (l + r) >> 1;
	build(ls, l, mid);
	build(rs, mid + 1, r);
}

void modify(int p, int l, int r, int k) {
	if (l <= tree[p].l && tree[p].r <= r) {
		tree[p].cnt += k;
		push_up(p);
		return; 
	}
	int mid = (tree[p].l + tree[p].r) >> 1;
	if (l <= mid) modify(ls, l, r, k);
	if (r > mid) modify(rs, l, r, k);
	push_up(p);
}

int get_rk(int x) {
	return lower_bound(b + 1, b + n2 + 1, x) - b;
}

signed main() {
	cin >> n;
	_for(i, 1, n) cin >> a[i];
	_for(i, 1, n) l[i] = vis[a[i]], vis[a[i]] = i;
	fill(vis, vis + N - 5, n + 1);
	_pfor(i, n, 1) r[i] = vis[a[i]], vis[a[i]] = i;
	
	_for(i, 1, n) b[++n2] = r[i], b[++n2] = i;
	sort(b + 1, b + n2 + 1);
	n2 = unique(b + 1, b + n2 + 1) - b - 1;
	_for(i, 1, n) r[i] = lower_bound(b + 1, b + n2 + 1, r[i]) - b;
	_for(i, 1, n) {
		ed[++tot] = {l[i], get_rk(i), r[i], 1};
		ed[++tot] = {i, get_rk(i), r[i], -1};
	}
	sort(ed + 1, ed + tot + 1, cmp);
	build(1, 1, n2);
	_for(i, 1, tot) {
		modify(1, ed[i].l, ed[i].r - 1, ed[i].val);
		if (i < tot) res += (ed[i + 1].x - ed[i].x) * tree[1].len;
	}
	cout << res << endl;
}

标签:AtCoder,return,Beginner,Contest,int,tree,mid,vis,dp
From: https://www.cnblogs.com/stOtue/p/18095419

相关文章

  • Atcoder ABC 346 全题解
    闲话上一篇全题解反向不错,如果大家支持我就会继续更。我ABC也打了,ARC也打了,没打好,疯狂掉大分……包括本场比赛也是整整补了EFG三道题,以及ARC死磕D结果使赛后五分钟AC又有素材了……A懒得讲B由于我被B题坑了,所以在此纪念。最简单的方法就是把字符串复制......
  • AtCoder Regular Contest 173 E Rearrange and Adjacent XOR
    洛谷传送门AtCoder传送门不妨考虑最后的结果可以成为哪些\(a_i\)的组合。为了方便分析,我们令\(a_i=2^{i-1}\)。进行一次操作后,所有\(\text{popcount}(a_i)\)都为偶数。所以一个\(x\in[0,2^n-1]\)能被生成出来的必要条件是\(\text{popcount}(x)\)为偶数。然......
  • AtCoder Beginner Contest 346 (ABCDEF)
    AtCoderBeginnerContest346A-AdjacentProduct题意给你一个数组a1,a......
  • Atcoder ABC144E Gluttony
    [ABC144E]Gluttony题面翻译【题目描述】高桥君参加大胃王比赛。比赛由\(N\)人组成的团队为基本单位参赛,高桥君的队伍的队员从\(1\simN\)编号。第\(i\)名队员的消化代价为\(A_i\)。比赛有\(N\)种不同的食物,每位队员需要负责吃掉其中一种食物,不能有两名队员吃同一种......
  • AtCoder Beginner Contest 346 题解
    A-AdjacentProductQuestion给你\(N\)个整数\(A_1,A_2,\dots,A_N\)。同时,定义\(B_i=A_i\timesA_{i+1}\(1\leqi\leqN-1)\)按此顺序打印\(B_1,B_2,\dots,B_{N-1}\)Solution按照题意模拟Code#include<bits/stdc++.h>usingnamespacestd;intmain......
  • Weekly Contest 390
    ProblemA每个字符最多出现两次的最长子字符串思路双指针,使用一个数组记录每个字符的出现次数,当出现次数大于2时l往左收缩其余情况往右划代码classSolution{publicintmaximumLengthSubstring(Strings){intn=s.length();int[]cnt=newint......
  • AtCoder Beginner Contest 346
    AtCoderBeginnerContest346最刺激的一集。尝试挑战手速极限,用了57s做A。但是好像还是很慢。然后做B,仍然想挑战手速。结果一眼出思路只要把wbwbwwbwbwbw多重复几遍就可以代替「无限长」。很快就写完了。然后交了三发罚时。后来发现我复制若干遍wbwbwwbwbwbw的时候......
  • AtCoder Beginner Contest 346
    A-AdjacentProduct(abc346A)题目大意给定\(n\)个数,依次输出相邻俩数的乘积。解题思路按照题意模拟即可。神奇的代码#include<bits/stdc++.h>usingnamespacestd;usingLL=longlong;intmain(void){ios::sync_with_stdio(false);cin.tie(0);c......
  • Atcoder ABC242H Random Painting
    对于这个\(\max\)似乎没有什么好的办法,考虑\(\min-\max\)反演。记\(t_i\)为第\(i\)格被染黑的时间,有\(E(\max(t_i))=\sum\limits_{S}(-1)^{|S|+1}E(\min(t_i))(i\inS)\)。考虑如果知道了\(S\),那么就可以知道\(c=\sum\limits_{i=1}^m[[l_j,r_j]\capS\no......
  • Atcoder ARC132E Paw
    考虑最后往左走往右走的覆盖情况。能发现肯定是有两个洞之间,或者是第一个洞左边,最后一个洞右边没有被覆盖,而左边的都被覆盖为向左,右边的都被覆盖为向右。大致证明就是考虑左边这一部分,如果有向右的,那么其右边的洞肯定都需要走过才行,不然会被覆盖,那么这样就可以一次性走出左边,就......