六校联考 20251105C. 物品采购(judge)

为什么写这个题解呢,因为感觉真的很久都没有写过这种屎长代码了。

\(type=1\)

手画不难发现,如果一个序列有 \(\le 2\) 个颜色段,那么它的贡献是 \(0\),因为任意一个子序列都会是子段。

否则贡献是 \(n-2\),因为可以任意漏选中间颜色段的一个元素,时间复杂度 \(O(n)\)

\(type=2\)

直觉上告诉我们,序列应该形如 \(S_0,x,S_1,x,S_2\),我们可以选择 \(S_0+x\) 或者 \(x+S_2\)

这样应该是最优的,如果我们选 \(S_0,x\) 之后还选了 \(y\),那么完全可以 \(S_0:=S_0+x,y:=x\)

需要注意不能出现 \(S_0=S_2=\varnothing\),扫一遍即可,时间复杂度 \(O(n)\)

\(type=3\)

考虑对 \(type=1\) 计数,首先容斥,总贡献为所有子序列的长度之和,设 \(n\) 的答案为 \(f_n\)

那么显然有递推式:\(f_n = 2f_{n-1} + 2^{n-1}\),求出来即可。

然后首先可以扣掉颜色段数为 \(1\) 的子序列,接下来考虑扣掉颜色段数为 \(2\) 的子序列。

不妨枚举第一个颜色段的末尾下标 \(p\),对于 \(p\) 前面的限制是选若干个等于 \(p\) 的,对 \(p\) 后面的限制是选若干个一样的。

因此维护 \(pre_{0/1,i},suf_{0/1,i}\) 代表前后缀的贡献和和方案数,记得最后要减去一个总方案数。

扫一遍前后缀即可,时间复杂度 \(O(n)\)

\(type=4\)

前面的三个部分应该都是平凡的,首先可以 \(O(2^nn)\) 暴力,然后场上根据暴力想了一个 \(O(n^4) \sim O(n^5)\) 的做法。

大概是枚举前面两个相同的,后面两个相同的下标算贡献,没写出来也不保证正确。

我们重新审视 \(type=2\) 的限制,发现这个限制并不方便计数,我们对其做转化:

\(k\) 为满足前 \(k\) 个两两不同,后 \(k\) 个两两不同的最大值。那么每个序列的贡献为 \(n-k-[k=n-1]\)

\([k=n-1]\) 的意义就是首尾相同,且中间都不同的序列答案应该是 \(0\)

对于这个式子,\(\sum n=f_n\) 已经在上面计算过,\([k=n-1]\) 可以枚举 \(p,q(p<q,a_p=a_q)\),设 \(x\)\((p,q)\) 的出现次数为 \(b_x\)

由于中间的数字两两不同,那么每个数 \(x\)\(b_x\) 种选择,或者干脆不选。那么答案即为 \(\sum_{p<q} \prod_{c \ne a_p} (b_c+1)\),这个可以在 \(O(n^3)\) 时间内计算。

现在考虑如何计算最难算的 \(\sum k\)。考虑差分贡献,把贡献 \(k\) 拆分到 \(1 \sim k\) 每个上面。我们枚举 \(p,q\) 代表第 \(k'(k' \le k)\) 个前缀和后缀下标的位置。

因为贡献被拆分了,所以我们不关心 \(k'\) 的值,我们只需要知道每种 \(k'\) 的方案之和加起来即为答案。

观察到 \(p,q\) 并不存在偏序关系,不妨先考虑 \(p<q\) 怎么做,考虑限制:

选择若干个数,强制选定 \(p,q\),使得 \([1,p],[q,n]\) 中选择的数的个数相等,并且 \([1,p]\) 选择的数互不相同,\([q,n]\) 选择的数互不相同,对 \((p,q)\) 无限制。

中间的 \((p,q)\) 区间贡献系数显然为 \(2^{q-p-1}\),我们把 \([1,p]\) 选择的个数减去 \([q,n]\) 的个数看成下标进行背包。

不过由于 \([1,p]\) 可以全部放在 \([q,n]\) 前面,可以看作是若干体积为 \(1\) 的物品。物品的系数类似上面的 \([k=n-1]\)

这样做一次背包是 \(O(n^2)\) 的,枚举 \(p,q\) 也是 \(O(n^2)\) 的,这样是 \(O(n^4)\) 的难以通过。

不过可以固定 \(p\),观察到 \(q\) 移动时物品更改个数是 \(O(1)\) 的,那么就可以回撤背包做到 \(O(n^3)\) 了。

继续观察 \(p>q\) 怎么做,考虑限制:

选择若干个数,强制选定 \(p,q\),使得 \([1,p],[q,n]\) 中选择的数的个数相等,并且 \([1,p]\) 选择的数互不相同,\([q,n]\) 选择的数互不相同。

跟上面不同的地方在于,如果在 \((q,p)\) 中区间选定一个 \(a_i=x\),那么 \([1,q),(p,n]\) 都不能再选定 \(x\)。反之也是。

也就是说,这个背包对于一种物品,有三种选择(\(+1,0,-1\) 体积,价值均不同),直接做仍然是 \(O(n^4)\) 的。

考虑这个背包怎么回撤?注意到背包等于乘上一个有交换律的稀疏矩阵,对这个稀疏矩阵高斯消元即可,消一次是 \(O(n)\) 的。

或者换一种表达方式,\(g_i = \sum af_{i-1}+bf_i +cf_{i+1}\),那么 \(g_{n+1}\) 只会由 \(af_{n}\) 转移过来,可以解出 \(f_n\)\(g_n\)\(af_{n-1}+bf_{n}\) 转移而来,可以解出 \(f_{n-1}\),依次类推。

因为这是背包删除一组加入过的物品,这个方程组应该是一定有唯一解的,时间复杂度 \(O(n^3)\)

顺提一嘴数据好像造的比较水,\(O(n^4)\) 卡卡也能乱日。

哦对了,实现上来说这个第一种情况是可以 merge 到第二种情况的,不过我过了也懒得改了。给个关键代码,为啥我写了 8KB 啊:

fv insert(i32 x) { rep (i, 0, n) inc(f[i], f[i + 1] * x % mod); }
fv erase(i32 x) { drep (i, n + 1, 1) dec(f[i - 1], f[i] * x % mod); }
fv insert2(i32 a, i32 b, i32 c) {
  if (!a && !b && !c) return ;
  b = (a * c + b + 1) % mod;
  rep (i, -n, n) h[N + i] = 0;
  rep (j, -n, n) h[N + j + 1] += f[N + j] * a;
  rep (j, -n, n) h[N + j - 1] += f[N + j] * c;
  rep (j, -n, n) f[N + j] = (h[N + j] + f[N + j] * b) % mod;
}
fv erase2(i32 a, i32 b, i32 c) {
  if (!a && !b && !c) return ;
  rep (i, -n, n) h[N + i] = 0;
  b = (a * c + b + 1) % mod;
  if (a > 0) {
    i32 inva = qkpow(a, mod - 2);
    drep (j, n + 1, -n) {
      h[N + j - 1] = (f[N + j] - h[N + j] * b % mod - h[N + j + 1] * c % mod) * inva % mod + mod;
      if (h[N + j - 1] >= mod) h[N + j - 1] -= mod;
    }
  } else {
    assert(b > 0);
    i32 invb = qkpow(b, mod - 2);
    drep (j, n, -n) {
      h[N + j] = (f[N + j] - h[N + j + 1] * c) % mod * invb % mod + mod;
      if (f[N + j] >= mod) h[N + j] -= mod;
    }
  }
  rep (i, -n, n) f[N + i] = h[N + i];
}
posted @ 2025-11-05 21:02  nullpt3  阅读(12)  评论(0)    收藏  举报