题意
- 输入一个长度为 n n n的序列 a i a_i ai,求有多少个长度大于等于 2 2 2的不上升子序列满足 ∏ i = 2 k ( a b i − 1 a b i )   m o d   2 > 0 \displaystyle\prod_{i = 2}^{k} \binom{a_{b_{i - 1}}}{a_{b_i}} \bmod 2 > 0 i=2∏k(abiabi−1)mod2>0.
用卢卡斯定理转化一下性质
发现这个东西模 2 2 2为 0 0 0,当且仅当前一个数存在一个二进制位为 0 0 0而后一个数那一位是 1 1 1
再转化一下那后一个数就是前一个数的子集,那么不妨设 f [ i ] f[i] f[i]为以 i i i结尾的序列数
直接枚举 i i i的子集转移即可 复杂度 O ( 3 l o g 2 m a x a ) O(3^{log_2^{maxa}}) O(3log2maxa)
大概是 O ( 3 18 ) O(3^{18}) O(318) 看起来这个复杂度比较虚但实际上
这个 d p dp dp常数很小而且又有两秒而且复杂度又没有卡满 就过去了
Codes
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10;
const int mod = 1e9 + 7;
int a[N], n, f[N];
int add(int x, int y) {
return x + y >= mod ? x + y - mod : x + y;
}
int main() {
#ifdef ylsakioi
freopen("3773.in", "r", stdin);
freopen("3773.out", "w", stdout);
#endif
int ans = 0;
scanf("%d", &n);
for(int i = 1; i <= n; ++ i) {
scanf("%d", &a[i]); ++ f[a[i]];
for(int sub = a[i]; sub; (sub -= 1) &= a[i])
if(sub != a[i])
f[sub] = add(f[sub], f[a[i]]);
ans = add(ans, f[a[i]] - 1);
}
cout << ans << endl;
return 0;
}
还是学一学复杂度比较正的正解做法吧
毕竟 C C F CCF CCF的老爷机是不会有 L O J LOJ LOJ这样的速度
一般二进制集合相关的题目都可以采用 m e e t i n m i d d l e meet \ in \ middle meet in middle来优化
这样子复杂度直接变成了根号
像这道题 我们记 f [ i ] [ j ] f[i][j] f[i][j]为前 9 9 9位是 i i i,后 9 9 9位为 j j j子集的方案数,那么每次枚举 i i i的超集记下来之后再转移到 j j j的子集上,那么这个复杂度就是 O ( 6 9 ) O(6^9) O(69),就可以过了
Codes
#include<bits/stdc++.h>
using namespace std;
const int N = sqrt(3e5);
const int mod = 1e9 + 7;
const int all = (1 << 9) - 1;
int f[N][N], n;
int main() {
#ifdef ylsakioi
freopen("4903.in", "r", stdin);
freopen("4903.out", "w", stdout);
#endif
int x, y, ans = 0, z;
scanf("%d", &n);
for(int i = 1; i <= n; ++ i) {
int now = 0;
scanf("%d", &z);
x = (z >> 9), y = z & all;
for(int j = x; j <= all; j = (j + 1) | x)
(now += f[j][y]) %= mod;
(ans += now) %= mod; ++ now;
for(int j = y; ; j = (j - 1) & y) {
(f[x][j] += now) %= mod;
if(!j) break;
}
}
cout << ans << endl;
return 0;
}