1011模拟赛

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

希望以后能适当摸鱼(

T1:ARC124B XOR Matching 2

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

存在一个 \(1\sim n\) 的排列 \(p\) 使得 \(a_{p_i}\oplus b_{p_i}=x\)

这题图方便可以用 map,但 \(O(n^2\log n)\) 的复杂度,以 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 觉得如果有两个相邻的球颜色相同很丑。 他每次可以交换相邻两个球,问至少交换多少次才能不丑。

\(1\le n\le 400\)

数据范围确定了是一个 \(O(n^3)\) 左右的 dp。

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

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

于是考虑对这个最终状态进行 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(n^3\log n)\) 的,预处理后是 \(O(n^3)\) 的,都能过。

以下为二分代码。

#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\) 序列不同,因为如果每个人都给了球,每个人少给一个结果是一样的。所以为了不重复,规定只算至少有一个人不给球的方案数。

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

考虑 \(\prod^n_{i=1}x_i\) 的实际意义,即给完球后每个人再从自己现在有的球中选出一个球的方案数。

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

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

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

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

\[dp[i][0]=dp[i-1][0]\times \sum\limits^{a_{i-1}}_{j=0}j \]

\(i-1\) 个人需要从自己剩下的球中选,而剩下的球可能有 \(0\sim a_{i-1}\) 个。

\[dp[i][0]=dp[i-1][1]\times (a_{i-1}+1) \]

\(i-1\) 个人需要考虑给第 \(i\) 个人多少个球,有 \(a_{i-1}+1\) 种选择。

\[dp[i][1]=dp[i-1][0]\times\sum\limits^{a_{i-1}}_{j=1}j(a_{i-1}-j) \]

\(i-1\) 个人需要考虑给第 \(i\) 个人多少个球,有 \(a_{i-1}+1\) 种选择,给完了后,第 \(i\) 个人要从中选一个,第 \(i-1\) 个人要从自己剩下的球选一个。

\[dp[i][1]=dp[i-1][1]\times \sum\limits^{a_{i-1}}_{j=1}j \]

\(i-1\) 个人需要考虑给第 \(i\) 个人多少个球,给完了第 \(i\) 个人还要从中选一个。

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

\[\sum\limits^n_{i=1}i(n-i)=\sum\limits^n_{i=1}in-i^2=(\sum\limits^n_{i=1}i)\cdot n-\sum\limits^n_{i=1}i^2=\frac{n(n+1)}{2}-\frac{n(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]\) 最大值,每次询问 \(\sum\limits^r_{i=l} mx(i-t,i)\)

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

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

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

\(l_i\) 为满足 \(j<i\and a_i\le a_j\) 的最大 \(j\)\(r_i\) 为满足 \(i<j\and a_i<a_j\) 的最小 \(j\)

则对于当前的 \(t\)\(a_i\) 覆盖的区间即为 \([\max(i,l_i+t+1),\min(i+t,r_i-1)]\)

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

  1. \(i=l_i+t+1\)
  2. \(i+t=r_i-1\)
  3. \(l_i+t+1>r_i-1\)

对于每个 \(a_i\) 都有一个表达式,\(n\) 个表达式最多修改 \(3n\) 次。

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

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

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

时间复杂度 \(O(n\log n+q\log n+q\log q)\)

#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;
}
posted @ 2021-10-31 22:05  zqs2020  阅读(168)  评论(1编辑  收藏  举报