NOIP2018 模拟 Problem A 解题报告 求子序列
1.1 题目描述
给定序列 A, 求出 A 中本质不同的子序列 (包含空的子序列) 个数模
10^9+7的结果
一个序列 B 是 A 的子序列需要满足 A 删掉某些元素后能够得到 B.
两个子序列中对应位置的数都相同我们就认为这两个子序列本质相同.
1.2 输入格式
第一行包含一个整数 N , 代表序列的长度.
接下来一行 N 个整数, 第 i 个数代表 Ai
.
1.3 输出格式
输出一个整数代表答案.
1.4 样例输入
5
2 3 1 3 2
1.5 样例输出
27
1.6 数据范围
对于 20% 的数据, N 10.
对于 40% 的数据, N 20
对于 70% 的数据, N 100000; 1 Ai 100
对于 100% 的数据, N 1000000; 1 Ai N
这个题目实际上是求给定序列的所有子序列,考试时用的是枚举组合数+对序列hash的40分做法(然而并没有40分,事实证明非洲人是不适合用hash的)
后来才知道求一个序列的子序列有个公式
设f[i]表示到第i个数时所有的子序列个数
if(之前没有重复元素出现)
f[i]=f[i-1]*2+1;
else
f[i]=f[i-2]*2-f[该元素之前出现的位置-1];
以2 3 1 3 2为例
对2来说,它对答案的贡献只有它自己,为1,f[i]=1;
对3来说,它对答案的贡献是它自己和2连在一起的2,3,为2 , f[i]=3;
对1同上
对第二个3来说,第一个3之前的部分,既可以连第一个3,也可以连第二个3,所以它对答案的贡献是第一个3之后的部分,f[i]= f[i]=f[i-2]*2-f[第一个3出现的位置-1];
对第二个2同上
p.s:其实也可以用看成集合求子集,用2^n-1来理解
完整代码
#include<bits/stdc++.h> using namespace std; const int MAXN=105; int f[MAXN],a[MAXN],mark[MAXN],n; int main() { cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++) { if(!mark[a[i]]) f[i]=f[i-1]*2+1; else f[i]=f[i-1]*2-f[mark[a[i]]-1]; mark[a[i]]=i; } cout<<f[n]+1; return 0; }