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]\):
将他展开,出转移:
\(a_n\) 和 \(x_n\) 只和第 \(n\) 项有关,因此前两项为 \(a_nf_{n-1}\) 和 \(x_nf_{n-1}\),但是第三项不太好搞因为涉及到了前一项,我们把它拿出来,设:
考虑 \(g_n\) 的转移,再拆:
然后发现,\(x_na_n\),\(x_n^2\) 与前边项无关,直接统计即可,观察最后一项其实就是 \(x_ng_{n-1}\),从之前转移即可。
注意到 \(x_n\) 是要取遍 \([0,a_n]\) 的,所以有 \(a_n+1\) 次贡献次数,相当于要把这 \(a_n+1\) 次对 \(f_{n-1}\) 的计算汇总起来得到 \(f_n\):
\(g_n\) 是同理的:
设 \(S1(x) = \frac{x(x+1)}{2}\),\(S2(x) = \frac{x(x+1)(2x+1)}{6}\),
当 \(x_i \in [1,a_i]\) 时,只把 \(f_n\) 的贡献次数那里调整为 \(a_n\) 次即可。
然后在 \(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\) 个人选的方案数。
考虑四种转移:(以下指算所有方案的转移,每个人至少给一个的稍微改改就好)
第 \(i−1\) 个人需要从自己剩下的球中选,而剩下的球可能有 \(0\sim a_{i-1}\) 个。
第 \(i−1\) 个人需要考虑给第 \(i\) 个人多少个球,有 \(a_{i-1}+1\) 种选择。
第 \(i−1\) 个人需要考虑给第 \(i\) 个人多少个球,有 \(a_{i-1}+1\) 种选择,给完了后,第 \(i\) 个人要从中选一个,第 \(i−1\) 个人要从自己剩下的球选一个。
第 \(i−1\) 个人需要考虑给第 \(i\) 个人多少个球,给完了第 \(i\) 个人还要从中选一个。
以上第三个方程还不能直接计算。化一下式子:
然后就可以直接算了。
但这题是一个环,我们先枚举 \(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
原题火事,今天没时间了,以后有空改。