[CTSC2017] 吉夫特 题解
前言
题目链接:洛谷。
提供一种更好想的方法,并且不受 \(a_i\) 不相同这一限制。
题意简述
给定长为 \(n\) 的序列 \(\{a_n\}\),设 \(p=\{x_k\}\) 为 \(a\) 的一个长度大于一的不上升子序列,即 \(k\ge2,\forall i\in[1,k),a_{x_i}\ge a_{x_{i+1}}\)。求:
对 \(10^9+7\) 取模的结果。
\(n\leq2.2\times10^5,a_i\lt2^{18}\),\(a_i\) 互不相同。
题目分析
记 \(V=\log\max a_i\)。
组合数模 \(2\) 不难想到 lucas 定理拆掉,\(\binom{n}{m}\bmod2\) 有值等价于在 \(2\) 进制拆分下,\(m\) 是 \(n\) 的子集。
考虑 DP,设 \(f_{i,j}\) 表示考虑前 \(i\) 个数,选出不上升子序列的末尾为 \(j\) 的方案数。转移可以 \(\mathcal{O}(2^V)\) 枚举 \(s\),判断 \(s\) 是否为 \(a_i\) 的超集,从 \(f_{i-1,s}\) 转移来,或者 \(i\) 不选可以继承 \(i-1\)。(如果把 \(i\) 这一维滚掉,使用枚举超集的方法做,由于 \(a_i\) 互不相同,就能做到总体 \(\mathcal{O}(3^V)\) 从而通过此题。)
时间 \(\mathcal{O}(n2^V)\),考虑优化。部分分 \(n\) 很小,\(f_i\) 是稀疏的,考虑直接枚举 \(j\),从 \(f_{i-1,a_j}\) 转移来。时间 \(\mathcal{O}(n^2)\)。
考虑每次能不能 \(\mathcal{O}(1)\) 转移,这要求我们维护高维后缀和,做一次就是 \(\mathcal{O}(V2^V)\) 的。
诶,我们根号分治一手,每 \(B\) 个就暴力重构高维后缀和,没有被高维后缀和包含的暴力转移,就能做到 \(\mathcal{O}(nB+\frac{n}{B}(n+V2^V))\)。
取 \(B=\sqrt{n+V2^V}\) 最优,时间复杂度为 \(\mathcal{O}(n\sqrt{n+V2^V})\),比较极限。
代码
#include <cstdio>
#include <iostream>
#include <cmath>
using namespace std;
const int mod = 1e9 + 7;
inline void O(int &a, int b) {
    a += b, a = a >= mod ? a - mod : a;
}
const int N = 2.2e5 + 10, V = 1 << 18;
const int NB = N;
int n, a[N];
int siz, tot, bl[N];
int L[NB], R[NB];
int f[N], g[V];
inline void rebuild(int p) {
    for (int i = 0; i < V; ++i)
        g[i] = 0;
    for (int i = 1; i <= p; ++i)
        g[a[i]] = f[i];
    for (int j = 0; j < 18; ++j)
        for (int s = V - 1; ~s; --s)
            if (s & 1 << j)
                O(g[s ^ 1 << j], g[s]);
}
int main() {
    scanf("%d", &n);
    siz = sqrt(n + 18 * V);
    tot = (n - 1) / siz + 1;
    for (int i = 1, j = 0; i <= tot; ++i) {
        L[i] = j + 1;
        R[i] = j += siz;
    }
    R[tot] = n;
    for (int i = 1; i <= n; ++i) {
        for (int j = L[i]; j <= R[i]; ++j) {
            bl[j] = i;
        }
    }
    int ans = mod - n;
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        f[i] = 1;
        O(f[i], g[a[i]]);
        for (int j = L[bl[i]]; j < i; ++j) {
            if ((a[j] & a[i]) == a[i])
                O(f[i], f[j]);
        }
        O(ans, f[i]);
        if (i < n && i == R[bl[i]]) rebuild(i);
    }
    printf("%d", ans);
    return 0;
}
本文作者:XuYueming,转载请注明原文链接:https://www.cnblogs.com/XuYueming/p/19036845。
若未作特殊说明,本作品采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。

                
            
        
浙公网安备 33010602011771号