视频加载失败

安吉D10-T4

708 字
4 分钟
安吉D10-T4
原题呈现

题目描述#

输入一个长度为 nn 的数列 a1,a2,⋯ ,ana_1, a_2, \cdots, a_n,问有多少个长度大于等于 22 的不上升的子序列满足:

∏i=2k(abi−1abi) mod 2>0\prod_{i=2}^{k} \binom{a_{b_{i-1}}}{a_{b_i}} \bmod 2 > 0

输出这个个数对 10000000071000000007 取模的结果.

输入格式#

第一行一个整数 nn.

接下来 nn 行,每行一个整数,第 ii 行表示 aia_i.

输出格式#

一行一个整数表示答案.

样例#

样例输入#

4
15
7
3
1

样例输出#

11

数据范围#

  • 前 10%10\% 的测试点:n≤9n \le 9,1≤ai≤131 \le a_i \le 13.
  • 前 20%20\% 的测试点:n≤17n \le 17,1≤ai≤201 \le a_i \le 20.
  • 前 40%40\% 的测试点:n≤1911n \le 1911,1≤ai≤40001 \le a_i \le 4000.
  • 前 70%70\% 的测试点:n≤2017n \le 2017.
  • 前 85%85\% 的测试点:n≤100084n \le 100084.
  • 100%100\% 的测试点:1≤n≤2119851 \le n \le 211985,1≤ai≤2333331 \le a_i \le 233333.

所有的 aia_i 互不相同,即不存在 i≠ji \neq j 满足 ai=aja_i = a_j.

我们可以尝试使用 dp 来解决子序列问题.

定义 dp[i] 表示以第 ii 个数结尾能够有多少满足要求的子序列.由于题意要求所有组合数的乘积模 2 大于 0,那就是要求所有组合数都是奇数.

转移方程为:

dp[i]=1+∑jj<idp[j],(ji) mod 2=1dp[i]=1+\sum_{j}^{j <i}dp[j],\binom{j}{i}\bmod 2=1

可以使用递推公式快速算出组合数.

这样能够拿到 40 pts.当 aia_i 到二十万的数据规模就无法处理了,那该怎么办呢?

Lucas 定理:

(xy) mod p=(⌊x/p⌋⌊y/p⌋)(x mod py mod p) mod p\binom{x}{y}\bmod p=\binom{\lfloor x/p\rfloor}{\lfloor y/p\rfloor}\binom{x\bmod p}{y\bmod p}\bmod p

当 p=2p=2 时,

(xy) mod 2=(⌊x/2⌋⌊y/2⌋)(x mod 2y mod 2) mod p\binom{x}{y}\bmod 2=\binom{\lfloor x/2\rfloor}{\lfloor y/2\rfloor}\binom{x\bmod 2}{y\bmod 2}\bmod p

将 (⌊x/2⌋⌊y/2⌋)\binom{\lfloor x/2\rfloor}{\lfloor y/2\rfloor} 拆解,可得:

(xy) mod 2=(⌊x/4⌋⌊y/4⌋)(⌊x/2⌋ mod 2⌊y/2⌋ mod 2)(x mod 2y mod 2) mod p\binom{x}{y}\bmod 2=\binom{\lfloor x/4\rfloor}{\lfloor y/4\rfloor}\binom{\lfloor x/2\rfloor\bmod 2}{\lfloor y/2\rfloor\bmod 2}\binom{x\bmod 2}{y\bmod 2}\bmod p

像这样拆解,最终可得:

(xy) mod 2=∏(xiyi)\binom{x}{y}\bmod 2=\prod\binom{x_i}{y_i}

其中,xi,y1x_i,y_1 分别表示 x,yx,y 中二进制表示的第 ii 位.

当且仅当 xi=0,yi=1x_i=0,y_i=1 时,(xiyi)=0\binom{x_i}{y_i}=0,故 (xy) mod 2=1\binom{x}{y}\bmod 2=1 等价于 ∀i,xi=0⇒yi=0\forall i,x_i=0\Rightarrow y_i=0,即位运算中 x∧y=yx\wedge y=y. 其实就是 xx 是 yy 二进制中的子集.

因此,在 dp 时,尝试子集枚举.具体来说,遍历到 a[i]a[i] 时,先将 f[i]f[i] 增加 1,表示 a[i]a[i] 单独成一组.再枚举 a[i]a[i] 的所有子集 kk,若 kk 在后面出现过(设其处于 a[j]a[j]),则说明 f[i]f[i] 可以转移到 f[j]f[j],将 f[i]f[i] 贡献给 f[j]f[j].

子集枚举可以使用下面的代码:

for (int j = (a[i] - 1) & a[i]; j > 0; j = (j - 1) & a[i])

请注意:由于题目中所要求的序列长度大于等于 2,因此最终答案需要减去 nn 个长度为 1 的序列.

标程

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 240000;
const int MOD = 1e9 + 7;
int n, a[N], id[N], f[N], ans;
signed main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
id[a[i]] = i;
}
for (int i = 1; i <= n; i++) {
(f[i] += 1) %= MOD;
(ans += f[i]) %= MOD;
for (int j = (a[i] - 1) & a[i]; j > 0; j = (j - 1) & a[i]) {
if (id[j] > i)
(f[id[j]] += f[i]) %= MOD;
}
}
cout << ans - n;
return 0;
}

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

安吉D10-T4
https://blog.jerrylab.top/posts/problem/anji2026/D10/T4/
作者
Jerry
发布于
2026-08-10
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Jerry
Hello, I'm Jerry.
公告
欢迎来到我的博客!这是一则示例公告。
分类
标签
最新动态
站点统计
文章
80
分类
3
标签
35
总字数
89,198
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Cloudflare Pages
博客版本
Firefly v6.16.5
文章许可
CC BY-NC-SA 4.0