2024.7.30

离谱,\(100+10+30+36\)\(\text{rk}4\)

好笑的,我 \(\text{T1}\) 一上来五分钟打的暴力就已经 \(\text{A}\) 了,我还花了好久来优化和对拍,为什么数据水的不行还给 \(4\text{s}\) 放了一车纯暴力啊……

T1 F

题意:给出两个序列 \(a_i\)\(b_i\) 其中 \(b\) 可以乱排。 求出所有可能的 \(x\) 使 \(\forall i \in[1,n],a_i \oplus b_i = x\),递增输出。 \(n\le 2000,0 \le a_i,b_i \le 10^9\)

暴力就给 \(b\) 全排列一下,边跑边判发现不行直接退掉,时间复杂度小于 \(O(n!)\)。期望得分 \(10 \text{pts}\),跑不满所以理想得分 \(20 \text{pts}\) 实际得分 100pts

(其实在原数据下,交的那发暴力第一个点就错了,理论上连 \(10\text{pts}\) 都不会有,因为没判重。)

优化复杂度,想到了一个 \(O(n^3)\) 的,那么就让每一个 \(b_i\) 依次和 \(a_1\) 匹配,然后再分别枚举其他的 \(a\)\(b\),若发现恰能完全匹配,则将 \(a_1 \oplus b_i\) 计入答案序列。能过 \(60\) 分了,但明显相当的跑不满,还有可能过,\(\text{T1}\) 就放个这?

自己随手造了个数据,发现有好多重复的答案啊,就去了个重。

学校 \(\text{OJ}\) 最慢的点跑了 \(9\text{ms}\),还是不理解为啥时限开 \(4\text{s}\),然后原题的加强版数据也能过。

T2 S

题意:给一个序列,由 \(\text{RGY}\) 三种元素组成,求至少交换多少次相邻元素才能使 \(\text{R}\),\(\text{G}\),\(\text{Y}\) 各自两两不相邻?\(1\le n \le 400\)

这不一眼 \(\text{dp}\),差不多 \(n^3\) 的,可以发现:无论怎样交换,相同颜色的球的相对位置不会改变。因为交换相同颜色的球显然是不优的。这样问题就可以转化为:在长度为 \(n\) 的序列中填上三种颜色,每种颜色数量固定,填颜色的价值定义为 逆序对的个数:这个性质比较 nb,不会证但是对的。且相邻位置颜色不同。具体而言,将原序列编号,第 \(i\)\(j\) 种球就对应它位置上的编号 \(id\)。填完颜色后,第 \(i\)\(j\) 种球有了新的位置 \(k\),就令
\(p_k=id\),答案就是 \(p\) 中 逆序对个数.

状态设 \(f_{i,j,k,1/2/3}\),其中 \(i,j,k\) 分别为三种颜色放了多少个,\(1/2/3\) 表示第 \(i+j+k\) 个位置放的颜色是哪种。

可以前缀和也可以二分去优化。

T3 Y

神仙 \(\text{dp}\),不难发现每个人传球个数都不为零时,可以全部少传 \(1\) 个球,显然没有影响。那就可以只考虑存在人不传球的情况。

考虑容斥,我们设 \(x_i\) 表示第 \(i\) 个人向右传球的数量,用 \(\forall x_i \in [0,a_i]\) 的贡献减去 \(\forall x_i \in [1,a_i]\) 的贡献,就可以得到 \(\exist x_i \in [0,a_i]\) 的答案了。

计算答案,对于 \(\forall x_i \in [0,a_i]\)

\[f_n = \prod_{i=1}^{n}(a_i-x_i+x_{i-1}) \]

将他展开,出转移:

\[f_n =\prod_{i=1}^{n-1}(a_i-x_i+x_{i-1})\cdot (a_n-x_n+x_{n-1}) \]

\[=a_n\prod_{i=1}^{n-1} (a_i-x_i+x_{i-1})-x_n\prod_{i=1}^{n-1} (a_i-x_i+x_{i-1})+x_{n-1}\prod_{i=1}^{n-1} (a_i-x_i+x_{i-1}) \]

\(a_n\)\(x_n\) 只和第 \(n\) 项有关,因此前两项为 \(a_nf_{n-1}\)\(x_nf_{n-1}\),但是第三项不太好搞因为涉及到了前一项,我们把它拿出来,设:

\[g_n=x_n\prod_{i=1}^n (a_i-x_i+x_{i-1}) \]

考虑 \(g_n\) 的转移,再拆:

\[g_n=x_n\prod_{i=1}^{n-1}(a_i-x_i+x_{i-1})\cdot (a_n-x_n+x_{n-1}) \]

\[=x_na_n\prod_{i=1}^{n-1} (a_i-x_i+x_{i-1})-x_n^2 \prod_{i=1}^{n-1} (a_i-x_i+x_{i-1})+x_nx_{n-1}\prod_{i=1}^{n-1} (a_i-x_i+x_{i-1}) \]

然后发现,\(x_na_n\)\(x_n^2\) 与前边项无关,直接统计即可,观察最后一项其实就是 \(x_ng_{n-1}\),从之前转移即可。

\[\begin{cases} g_n = (x_na_n-x_n^2) f_{n-1} + x_ng_{n-1} \\ f_n = (a_n-x_n)f_{n-1}+g_{n-1} \\ x_n=\sum_{i=0}^{a_n}i \end{cases} \]

注意到 \(x_n\) 是要取遍 \([0,a_n]\) 的,所以有 \(a_n+1\) 次贡献次数,相当于要把这 \(a_n+1\) 次对 \(f_{n-1}\) 的计算汇总起来得到 \(f_n\):

\[f_n = (a_n\cdot (a_n+1) - \sum_{i=0}^{a_n}i)f_{n-1}+(a_n+1)g_{n-1} \]

\[=\frac{a_n\cdot(a_n+1)}{2}f_{n-1} + (a_n+1)g_{n-1} \]

\(g_n\) 是同理的:

\[g_n = (\frac{a_n\cdot(a_n+1)}{2}a_n-\frac{a_n(a_n+1)(2a_n+1)}{6})f_{n-1}+\frac{a_n\cdot(a_n+1)}{2}g_{n-1} \]

\(S1(x) = \frac{x(x+1)}{2}\)\(S2(x) = \frac{x(x+1)(2x+1)}{6}\)

\[\begin{cases} f_n = S1(a_n)f_{n-1}+(a_n+1)g_{n-1} \\ g_n = (S1(a_n)a_n-S2(a_n))f_{n-1}+S1(a_n)g_{n-1} \end{cases} \]

\(x_i \in [1,a_i]\) 时,只把 \(f_n\) 的贡献次数那里调整为 \(a_n\) 次即可。

\[\begin{cases} f_n = S1(a_n-1)f_{n-1}+a_ng_{n-1} \\ g_n = (S1(a_n)a_n-S2(a_n))f_{n-1}+S1(a_n)g_{n-1} \end{cases} \]

然后在 \(n\) 处断环成链,容斥 \(x_i\) 取值计算。我们先枚举 \(1\) 是从自己剩下的球选还是从 \(n\) 给的球选,两种方案相加即可。。

这种做法这个后面这里真的不是很懂,想也不明白,估计得一直想下去,啥时候懂了或者谁讲明白了来补坑吧。。

  int n = read();
	inv2 = qp(2,MOD-2);
	inv6 = qp(6,MOD-2);
//	cout<<inv2<<" "<<inv6<<endl;
	for(int i{1};i<=n;i++)
		a[i] = read();
	a[n+1] = a[1];
	int ans{};
	f[1] = 1;
	for(int i{2};i<=n+1;i++)
	{
		f[i] = (s1(a[i]) *f[i-1]%MOD + (a[i] + 1) * g[i-1] % MOD ) % MOD;
		g[i] = ((s1(a[i]) * a[i] % MOD - s2(a[i]) + MOD) * f[i-1] % MOD + s1(a[i]) * g[i-1] % MOD) % MOD;
	}
	(ans += f[n+1]) %= MOD;
	
	memset(f,0,sizeof(f));
	memset(g,0,sizeof(g));
	g[1] = 1;
	for(int i{2};i<=n+1;i++)
	{
		f[i] = (s1(a[i]) *f[i-1]%MOD + (a[i] + 1) * g[i-1] % MOD ) % MOD;
		g[i] = ((s1(a[i]) * a[i] % MOD - s2(a[i]) + MOD) * f[i-1] % MOD + s1(a[i]) * g[i-1] % MOD) % MOD;
	}
	
	(ans += g[n+1]) %= MOD;
	
	memset(f,0,sizeof(f));
	memset(g,0,sizeof(g));
	
	f[1] = 1;
	for(int i{2};i<=n+1;i++)
	{
		f[i] = (f[i-1]*(s1(a[i]-1)) + a[i]* g[i-1]) % MOD;
		g[i] = ((s1(a[i]) * a[i] % MOD - s2(a[i]) + MOD) * f[i-1] % MOD + s1(a[i]) * g[i-1] % MOD) % MOD;
	}
	ans = (ans - (f[n+1]) % MOD + MOD) % MOD;
	memset(f,0,sizeof(f));
	memset(g,0,sizeof(g));
	
	g[1] = 1;
	for(int i{2};i<=n+1;i++)
	{
		f[i] = (f[i-1]*(s1(a[i]-1)) + a[i]* g[i-1]) % MOD;
		g[i] = ((s1(a[i]) * a[i] % MOD - s2(a[i]) + MOD) * f[i-1] % MOD + s1(a[i]) * g[i-1] % MOD) % MOD;
	}
	ans = (ans - (g[n+1]) % MOD + MOD) % MOD;
	writeln(ans);

另外一种做法:

考虑 \(\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]\cdot \sum_{j=1}^{a_{i-1}} j (a_{i-1}-j) \]

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

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

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

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

\[\sum\limits^n_{i=1}i(n-i)= \sum^n_{i=1}in-i^2=( \sum^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\) 给的球选,两种方案相加即可。
这种倒是好想,但是这两种实现为什么大差不差啊!

#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 O

原题火事,今天没时间了,以后有空改。

posted @ 2024-09-14 21:22  WanGMiNgWeI  阅读(8)  评论(0编辑  收藏  举报