子序列(混合递推+前缀和 || 分块)

问题

  • 序列 a 1 , a 2 , ⋯ , a n a_1,a_2,\cdots ,a_n a1a2an,求子序列中长度大于1的传奇序列的个数。 1 ≤ n ≤ 3 ∗ 1 0 5 , 0 ≤ a i < 2 18 1\leq n \leq 3*10^5, 0\leq a_i < 2^{18} 1n3105,0ai<218,结果对100000007取余。
  • 若序列 b 1 , b 2 , ⋯ , b k b_1,b_2,\cdots ,b_k b1b2bk 满足 b i    &    b j = b j    且    i < j b_i\; \&\; b_j = b_j \;且\;i<j bi&bj=bji<j ,则称为传奇序列

分析

  • 遍历子序列的一般方式:将子序列按结尾元素分成子空间,子空间数量 O ( n ) O(n) O(n)
  • & \& & 运算性质
    • a    &    b = b a\; \&\; b = b a&b=b,例如: a = 1110    1010 , b = 1100    0010 a = 1110\;1010,b = 1100\; 0010 a=11101010b=11000010
    • a    &    b = b , b    &    c = c a\; \&\; b = b,b\; \&\; c = c a&b=bb&c=c,那么 a    &    c = c a\; \&\; c = c a&c=c
    • 小于 a a a 的最大 b = ( a − 1 )    &    a b = (a-1)\;\&\;a b=(a1)&a,递推式 b i + 1 = ( b i − 1 )    &    a ( b i > b i + 1 ) b_{i+1} = (b_i-1)\;\&\;a\quad(b_i > b_{i+1}) bi+1=(bi1)&a(bi>bi+1)
    • 大于 b b b 的最小 a = ( b + 1 )    ∥    a a = (b+1)\;\|\;a a=(b+1)a,递推式 a i + 1 = ( a i + 1 )    ∥    a ( a i < a i + 1 ) a_{i+1} = (a_i+1)\;\|\;a\quad(a_i < a_{i+1}) ai+1=(ai+1)a(ai<ai+1)

暴力枚举

分析

  • f [ i ] f[i] f[i] 是以元素 a i a_i ai 结尾的的满足条件的子序列的数量(元素下标: i i i
  • g [ a ] g[a] g[a] 是以元素 a a a 结尾的的满足条件的子序列的数量(元素值: a a a
  • 则递推过程
    • 到哪里去?
      • g [ a ] g[a] g[a] 定义:能与值为 a a a 的元素构成传奇序列的子序列数量
      • f [ i ] = g [ a i ] + 1 f[i] = g[a_i]+1 f[i]=g[ai]+1
      • g [ a j ] = g [ a j ] + f [ i ] g[a_j] = g[a_j] +f[i] g[aj]=g[aj]+f[i],其中 j ∈ { i , i + k 1 , i + k 2 , ⋯   } , a i    &    a j = a j j \in \{i,i+k_1,i+k_2,\cdots \},a_i \;\&\;a_j=a_j j{ii+k1i+k2}ai&aj=aj,复杂度 O ( n ) O(n) O(n)
      • 可优化:序列的后续元素中可能不出现值为 a j a_j aj 的元素,能延迟操作更佳;依据查询的“组合”策略,进行组合内修改
    • 从哪里来?
      • g [ a ] g[a] g[a] 定义:尾元素值为 a a a传奇数列数量
      • f [ i ] = ∑ k g [ a k ] f[i] = \sum_k g[a_k] f[i]=kg[ak],其中 1 ≤ k ≤ i − 1 , a k    &    a i = a i 1 \leq k \leq i-1,a_k \;\&\;a_i=a_i 1ki1ak&ai=ai,复杂度 O ( n ) O(n) O(n)
      • g [ a i ] = g [ a i ] + f [ i ] g[a_i] = g[a_i] +f[i] g[ai]=g[ai]+f[i]
      • 可优化:某些数据可能被反复查询而且是可以成为一个固定的组合(前缀和就是一种组合)
  • 无论是哪种递推方式,复杂度都为: O ( n 2 ) O(n^2) O(n2)

混合递推+前缀和

分析

  • 对暴力枚举进行优化
  • 递推方法:" 到哪里去?“和"从哪里来?” 相结合
  • f [ i ] f[i] f[i] 定义不变,即以元素 a i a_i ai 结尾的的满足条件的子序列的数量(元素下标: i i i
  • g [ a ] ⇒ g [ A ] [ B ] g[a] \Rightarrow g[A][B] g[a]g[A][B] A 、 B A、B AB 分别等于元素值 a a a 的高9位和低9位
  • 例如:153629的高9位是 300 = 153629 > > 9 300=153629>>9 300=153629>>9,低9位是 29 = 153629    &    511 29=153629\;\&\;511 29=153629&511
  • g [ A ] [ B ] g[A][B] g[A][B] 的含义:
    • 已有的序列中尾元素的高9位是A且能与值为 a a a 的元素构成传奇序列的子序列数量(是一个组合
    • g [ A ] [ B ] = ∑ f [ i ] g[A][B] = \sum f[i] g[A][B]=f[i],其中 a [ i ] = ( A < < 9 ) + B k , B k    &    B = B a[i] = (A<<9) +B_k,B_k \;\&\;B=B a[i]=(A<<9)+BkBk&B=B
    • 类似于是一个前缀和或后缀和, 以153629为例,则 B k ∈ { 29 , 31 , ⋯ , 509 , 511 } B_k \in \{29,31,\cdots,509,511\} Bk{2931509511}
   29    31    61    63    93    95   125   127   157   159   189   191   221   223   253   255
  285   287   317   319   349   351   381   383   413   415   445   447   477   479   509   511
  • 递推过程:
    • A = a i > > 9 , B = a i    &    511 A = a_i >> 9, B = a_i\; \&\;511 A=ai>>9B=ai&511
    • f [ i ] = ∑ j = 1 k g [ A j ] [ B ] + 1 f[i] =\sum_{j = 1}^k g[A_j][B]+1 f[i]=j=1kg[Aj][B]+1,其中 A = A 1 < ⋯ < A k ≤ 511 , A j    &    A = A A = A_1 < \cdots < A_k \leq 511,A_j\; \&\; A = A A=A1<<Ak511Aj&A=A,复杂度 O ( n ) O(\sqrt{n}) O(n ),【从哪里来?】
    • g [ A ] [ B j ] = g [ A ] [ B j ] + f [ i ] g[A][B_j] =g[A][B_j] +f[i] g[A][Bj]=g[A][Bj]+f[i],其中 B = B 1 > ⋯ > B k ≥ 0 , B    &    B j = B j B = B_1 > \cdots > B_k \geq 0,B\; \&\; B_j = B_j B=B1>>Bk0B&Bj=Bj,复杂度 O ( n ) O(\sqrt{n}) O(n ),【到哪里去?】
  • 复杂度: O ( n n ) O(n\sqrt{n}) O(nn )

代码

#include<bits/stdc++.h>
using namespace std;
#define MD 1000000007
int n, a, g[520][520], ans;
int main(){
	int t, x, y, cur;
	scanf("%d", &t);
	while(t--){
		scanf("%d", &n);
		memset(g, 0, sizeof g);
        ans = 0;
		for(int i = 1; i <= n; ++i){
			scanf("%d", &a);
			x = a>>9, y = a&511, cur = 1;
			for(int j = x; j <= 511; j = (j+1)|x)
                cur = (cur + g[j][y]) % MD;  // cur: f[i]
			ans = (ans + cur)%MD;
			for(int j = y; ; j = (j-1)&y){
				g[x][j] = (g[x][j] + cur)%MD;
                if(j == 0) break;
            }
		}
		printf("%d\n", ans-n>0?(ans-n):(ans-n+MD));
	}	
    return 0;
}

分块

分析

  • 按尾元素下标可以递推

代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jpphy0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值