1011模拟赛

为什么10.11的比赛现在才补呢,主要是由于之前效率不高+摸鱼严重,导致之前膜你赛的补题差一堆。。。

希望以后能适当摸鱼(

T1:ARC124B XOR Matching 2

题意就是给了你两个长度为 n 的序列 a,b,然后找到所有满足以下条件的 x

存在一个 1n 的排列 p 使得 apibpi=x

这题图方便可以用 map,但 O(n2logn) 的复杂度,以 map 的常数不太彳亍,容易 TLE,所以我用了离散化。

#include <cstdio>
#include <algorithm>
#include <cstring>

int a[2005], b[2005], c[4000005], d[2005], s[2005], used[2005], len, cnt, n;
int ans[2005], tot;

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++ i) scanf("%d", a + i);
	for (int i = 1; i <= n; ++ i) scanf("%d", b + i);
	if (n == 1) return printf("%d %d\n", 1, a[1] ^ b[1]), 0;
	std::sort(b + 1, b + n + 1);
	b[0] = b[n + 1] = -1;
	for (int i = 1, j = 0, st = 0; i <= n + 1; ++ i)
		if (b[i] != b[i - 1]) s[st] = j, j = 1, st = i;
		else ++ j;
	for (int i = 1; i <= n; ++ i)
	for (int j = 1; j <= n; ++ j)
		c[++ len] = a[i] ^ b[j];
	std::sort(c + 1, c + len + 1);
	c[0] = c[len + 1] = -1;
	for (int i = 1, j = 0; i <= len + 1; ++ i)
		if (c[i - 1] != c[i]) {
			if (j >= n) d[++ cnt] = c[i - 1];
			j = 1;
		} else ++ j;
	for (int i = 1; i <= cnt; ++ i) {
		memset(used, 0, sizeof used);
		bool flag = true;
		for (int j = 1; j <= n; ++ j) {
			int k = std::lower_bound(b + 1, b + n + 1, a[j] ^ d[i]) - b;
			if (a[j] ^ d[i] ^ b[k]) {flag = false; break;}
			++ used[k];
		}
		if (flag) for (int j = 1; j <= n; ++ j)
			if (used[j] > s[j]) {flag = false; break;}
		if (flag) ans[++ tot] = d[i];
	}
	printf("%d\n", tot);
	for (int i = 1; i <= tot; ++ i) printf("%d\n", ans[i]);
	return 0;
}

T2:NKOJ8687 S

题面:
n 个球,每个球有 RGY 三种颜色,现在小 F 觉得如果有两个相邻的球颜色相同很丑。 他每次可以交换相邻两个球,问至少交换多少次才能不丑。

1n400

数据范围确定了是一个 O(n3) 左右的 dp。

直接 dp 会出各种问题,考虑如果确定最终状态我们需要交换多少次。这个可以用逆序对算。

具体而言,答案是这个序列的逆序对:
最终状态里的第 ix 字符,所代表的数是原序列第 ix 字符的位置。

于是考虑对这个最终状态进行 dp。设计状态方面,我们需要知道当前三种字符分别填到了哪里,最后一个填的是哪个字符。

dp[i][j][k][ed=0/1/2] 表示最后一个 R 在原序列第 i 个位置,最后一个 G 在原序列第 j 个位置,最后一个 Y 在原序列第 k 个位置。ed=0,1,2 分别表示最终状态中最后一个字符是 R/G/Y。

转移可以二分求逆序对,也可以预处理后 O(1) 求。二分求是 O(n3logn) 的,预处理后是 O(n3) 的,都能过。

以下为二分代码。

#include <cstdio>
#include <cstring>
#include <algorithm>

inline int abs(int k) {return k >= 0 ? k : -k;}
inline int min(const int x, const int y) {return x < y ? x : y;}
int dp[405][405][405][3], nxt[405], frt[3], lst[405], s[405], n;
int R[405], G[405], Y[405], pos[405], cntR, cntG, cntY;
char str[405];

int main() {
	memset(dp, 0x3f, sizeof dp);
	scanf("%d%s", &n, str + 1);
	for (int i = 1; i <= n; ++ i)
		if (str[i] == 'R') str[i] = 0, R[++ cntR] = i, pos[i] = cntR;
		else if (str[i] == 'G') str[i] = 1, G[++ cntG] = i, pos[i] = cntG;
		else str[i] = 2, Y[++ cntY] = i, pos[i] = cntY;
	for (int i = 1; i <= n; ++ i) {
		nxt[lst[str[i]]] = i, s[i] = s[lst[str[i]]] + 1, lst[str[i]] = i;
		if (!frt[str[i]]) frt[str[i]] = i;
	}
	dp[0][0][0][0] = 0;
	for (int i = 0; i <= n; ++ i) if (!i || str[i] == 0)
	for (int j = 0; j <= n; ++ j) if (!j || str[j] == 1)
	for (int k = 0; k <= n; ++ k) if (!k || str[k] == 2)
	for (int ed = 0; ed <= 2; ++ ed) {
		if (nxt[i] && ((!i && !j && !k) || ed != 0)) {
			int Next = i ? nxt[i] : frt[0];
			int tmp = dp[i][j][k][ed];
			if (j) tmp += pos[j] - (std::lower_bound(G + 1, G + pos[j] + 1, Next) - G) + 1;
			if (k) tmp += pos[k] - (std::lower_bound(Y + 1, Y + pos[k] + 1, Next) - Y) + 1;
			dp[Next][j][k][0] = min(dp[Next][j][k][0], tmp);
		}
		if (nxt[j] && ((!i && !j && !k) || ed != 1)) {
			int Next = j ? nxt[j] : frt[1];
			int tmp = dp[i][j][k][ed];
			if (i) tmp += pos[i] - (std::lower_bound(R + 1, R + pos[i] + 1, Next) - R) + 1;
			if (k) tmp += pos[k] - (std::lower_bound(Y + 1, Y + pos[k] + 1, Next) - Y) + 1;
			dp[i][Next][k][1] = min(dp[i][Next][k][1], tmp);
		}
		if (nxt[k] && ((!i && !j && !k) || ed != 2)) {
			int Next = k ? nxt[k] : frt[2];
			int tmp = dp[i][j][k][ed];
			if (i) tmp += pos[i] - (std::lower_bound(R + 1, R + pos[i] + 1, Next) - R) + 1;
			if (j) tmp += pos[j] - (std::lower_bound(G + 1, G + pos[j] + 1, Next) - G) + 1;
			dp[i][j][Next][2] = min(dp[i][j][Next][2], tmp);
		}
	}
	int ans = min(dp[lst[0]][lst[1]][lst[2]][0],
	min(dp[lst[0]][lst[1]][lst[2]][1], dp[lst[0]][lst[1]][lst[2]][2]));
	printf("%d", ans < 1e8 ? ans : -1);
	return 0;
}

T3 ARC124E Pass to Next

毒瘤题,对我而言理解起来非常困难,也是鸽到现在才搞懂。。。

这题看上去非常不可做,特别是那个奇奇怪怪的累乘,所以我们直接跳过先做一些初步分析,想办法干掉这个累乘:

首先每个人给下一个人的球数不同,不代表最后的 x 序列不同,因为如果每个人都给了球,每个人少给一个结果是一样的。所以为了不重复,规定只算至少有一个人不给球的方案数。

然而这玩意儿特别难算,所以用所有方案数减去每个人都给了球的方案数。

考虑 i=1nxi 的实际意义,即给完球后每个人再从自己现在有的球中选出一个球的方案数。

而现在有的球有可能是给完别人后剩下的,也有可能是别人给我的,所以分成两种 dp 计算:

dp[i][0] 表示前 i1 个人选完,第 i 个人从自己剩下的球中选方案数,dp[i][1] 表示前 i 个人选完,第 i 个人从别人给的球中选方案数

注意:一个是前 i1 人选完,一个是前 i 人选完,也就是说 dp[i][0] 并没有计算第 i 个人选的方案数。

考虑四种转移:(以下指算所有方案的转移,每个人至少给一个的稍微改改就好)

dp[i][0]=dp[i1][0]×j=0ai1j

i1 个人需要从自己剩下的球中选,而剩下的球可能有 0ai1 个。

dp[i][0]=dp[i1][1]×(ai1+1)

i1 个人需要考虑给第 i 个人多少个球,有 ai1+1 种选择。

dp[i][1]=dp[i1][0]×j=1ai1j(ai1j)

i1 个人需要考虑给第 i 个人多少个球,有 ai1+1 种选择,给完了后,第 i 个人要从中选一个,第 i1 个人要从自己剩下的球选一个。

dp[i][1]=dp[i1][1]×j=1ai1j

i1 个人需要考虑给第 i 个人多少个球,给完了第 i 个人还要从中选一个。

以上第三个方程还不能直接计算。化一下式子:

i=1ni(ni)=i=1nini2=(i=1ni)ni=1ni2=n(n+1)2n(n+1)(2n+1)6

然后就可以直接算了。

但这题是一个环,我们先枚举 1 是从自己剩下的球选还是从 n 给的球选,两种方案相加即可。

时间复杂度 O(n)

#include <cstdio>
#include <cstring>
#define int long long

const int mod = 1e9 + 7, inv2 = 500000004, inv6 = 166666668;
int n, a[1000005], f[1000005][2];
inline int s(int n) {return n * (n + 1) % mod * inv2 % mod;}
inline int t(int n) {return n * (n + 1) % mod * (2 * n + 1) % mod * inv6 % mod;}

int solve(int p, int q) {
	memset(f, 0, sizeof f);
	f[1][0] = p ^ 1, f[1][1] = p;
	for (int i = 2; i <= n; ++ i) {
		f[i][0] = (f[i - 1][1] * (a[i - 1] - q + 1) + f[i - 1][0] * s(a[i - 1] - q)) % mod;
		f[i][1] = (f[i - 1][1] * s(a[i - 1]) + f[i - 1][0] * (a[i - 1] * s(a[i - 1]) % mod - t(a[i - 1]))) % mod;
	}
	if (p == 0) return (f[n][1] * (a[n] - q + 1) + f[n][0] * s(a[n] - q)) % mod;
	else return (f[n][1] * s(a[n]) + f[n][0] * (a[n] * s(a[n]) % mod - t(a[n]))) % mod;
}

signed main() {
	scanf("%lld", &n);
	for (int i = 1; i <= n; ++ i) scanf("%lld", a + i);
	printf("%lld", ((solve(0, 0) + solve(1, 0) - solve(0, 1) - solve(1, 1)) % mod + mod) % mod);
	return 0;
}

T4:[JOI 2020 Final] 火事

把奇怪的题意转化一下,给你一个序列 a,令 mx(l,r) 表示 a 序列区间 [l,r] 最大值,每次询问 i=lrmx(it,i)

直接算不可取,自然想到对于每个 ai,算当前它覆盖到的区间。

所谓它覆盖的区间,指对于当前 t,所有 mx(jt,j)=ai(i[jt,j])j 构成的集合。显然这个集合是一段区间。特别地,当 ai=aj,当且仅当 i<j 我们认为 i 覆盖了 j

把询问离线下来,按 t 排序,从前往后推移这个 t

li 为满足 j<iaiaj 的最大 jri 为满足 i<jai<aj 的最小 j

则对于当前的 tai 覆盖的区间即为 [max(i,li+t+1),min(i+t,ri1)]

从前往后推移 t 时,当前 ai 覆盖的区间长度都可以表示为一个一次函数或常数。只有当如下三种情况时 ai 覆盖区间长度的表达式会发生变化:

  1. i=li+t+1
  2. i+t=ri1
  3. li+t+1>ri1

对于每个 ai 都有一个表达式,n 个表达式最多修改 3n 次。

考虑查询。[l,r] 区间的查询可以转化成 [1,r][1,l1]。因此考虑查询 [1,p]

首先我们用 st 表查出当前覆盖 p 位置的 ai,则 a1,a2...ai1 覆盖的区间一定都在 [1,p] 以内,答案先加上 1i1 所有表达式对于当前的 t 算出来的值。这里可以用树状数组维护表达式前缀和,一颗维护系数(斜率),一颗维护常数(截距)。

对于 ai,我们可以算出当前它覆盖的区间,也可以算出当前覆盖的区间有多少元素在 [1,p] 以内。然后 ai 的贡献也可以计算了。

时间复杂度 O(nlogn+qlogn+qlogq)

#include <cstdio>
#include <stack>
#include <vector>
#include <algorithm>
#define int long long

inline int max(const int x, const int y) {return x > y ? x : y;}
int n, q, a[200005], l[200005], r[200005], st[200005][18], Log[200005], ans[200005];
struct BIT {
	int c[200005];
	inline void update(int x, int d) {
		for (register int i = x; i <= n; i += (i & ~i + 1)) c[i] += d;
	}
	inline int query(int x) {
		register int sum = 0;
		for (register int i = x; i; i -= (i & ~i + 1)) sum += c[i];
		return sum;
	}
} slope, constant;
struct Quest {
	int l, r, t, id;
	inline bool operator < (const Quest a) const {return t < a.t;}
} que[200005];
std::vector<int> vec1[200005], vec2[200005], vec3[200005];
std::stack<int> s;

inline int query(int l, int r) {
	int k = Log[r - l + 1];
	return a[st[l][k]] >= a[st[r - (1 << k) + 1][k]] ? st[l][k] : st[r - (1 << k) + 1][k];
}
int getans(int p, int t) {
	if (!p) return 0;
	int k = query(max(1, p - t), p);
	return slope.query(k - 1) * t + constant.query(k - 1) + (p - max(k, l[k] ? l[k] + t + 1 : k) + 1) * a[k];
}

signed main() {
	scanf("%lld%lld", &n, &q);
	for (int i = 1; i <= n; ++ i) scanf("%lld", a + i), st[i][0] = i;
	for (int i = 2; i <= n; ++ i) Log[i] = Log[i >> 1] + 1;
	for (int i = n; i; -- i) {
		while (s.size() && a[s.top()] <= a[i]) l[s.top()] = i, s.pop();
		s.push(i);
	}
	while (s.size()) s.pop();
	for (int i = 1; i <= n; ++ i) {
		while (s.size() && a[s.top()] < a[i]) r[s.top()] = i, s.pop();
		s.push(i);
	}
	while (s.size()) r[s.top()] = n + 1, s.pop();
	for (int i = 1; i <= n; ++ i) {
		if (l[i]) vec1[i - l[i] - 1].push_back(i);
		vec2[r[i] - i - 1].push_back(i);
		if (l[i]) vec3[r[i] - l[i] - 1].push_back(i);
		slope.update(i, a[i]), constant.update(i, a[i]);
	}
	for (register int j = 1; 1 << j <= n; ++ j)
	for (register int i = 1; i + (1 << j) - 1 <= n; ++ i)
		if (a[st[i][j - 1]] >= a[st[i + (1 << j - 1)][j - 1]]) st[i][j] = st[i][j - 1];
		else st[i][j] = st[i + (1 << j - 1)][j - 1];
	for (int i = 1; i <= q; ++ i) scanf("%lld%lld%lld", &que[i].t, &que[i].l, &que[i].r), que[i].id = i;
	que[0].t = -1;
	std::sort(que + 1, que + q + 1);
	for (int i = 1; i <= q; ++ i) {
		for (int j = que[i - 1].t + 1; j <= que[i].t; ++ j) {
			for (int k : vec1[j]) slope.update(k, -a[k]), constant.update(k, (k - l[k] - 1) * a[k]);
			for (int k : vec2[j]) slope.update(k, -a[k]), constant.update(k, (r[k] - 1 - k) * a[k]);
			for (int k : vec3[j]) {
				slope.update(k, slope.query(k - 1) - slope.query(k));
				constant.update(k, constant.query(k - 1) - constant.query(k));
			}
		}
		ans[que[i].id] = getans(que[i].r, que[i].t) - getans(que[i].l - 1, que[i].t);
	}
	for (int i = 1; i <= q; ++ i) printf("%lld\n", ans[i]);
	return 0;
}

本文作者:zqs2020

本文链接:https://www.cnblogs.com/stinger/p/15490009.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   zqs2020  阅读(212)  评论(1编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.