前缀和

第一次(在牛客的数据结构课)系统地听完了一节讲前缀和的课,发现自己以前学了个寂寞

(正式)初次见面,请多关照!

 

知识点:

  1.前缀和 and 差分数组

 

   s0=a0,si=si-1+ai

 

    满足这样关系的s称为数组a的前缀和数组

   d0=a0,di=ai-1-ai

   满足这样关系的d称为数组a的差分数组

 

   前缀和和差分运算互为逆运算

   因为对a做一次差分,再对差分数组做一次前缀和运算得到原数组

 

   2.广义前缀和 and 应用

   只要每一次操作产生的影响覆盖之前的结果,不覆盖之后的;且有反向操作可以与这一次操作的效果抵消,就可视为是广义的前缀和

   应用:矩阵链乘、卷积(待学习)

 

   3.高阶前缀和

   ·求k阶前缀和 系数满足 C(k,k) C(k,k+1)...C(k,k+i-1)...C(k,k+n-1)

   ·可以用来静态维护多项式:n次多项式做n+1次差分后得到常数。据此先用差分数组标记,可以通过求n+1次前缀和得到ans

 

     4.高维前缀和

  考虑问题:

    定义一个集合的价值为:该集合所拥有的物品权值之异或和。

    现在智乃酱想问你m个问题,对于每个问题他都会给你一个k个物品集合U,他想让你告诉她U的所有子集的价值之和,U的所有超集的价值之和。在本问题中,全集的定义为这N个物品组成的集合。(1<=N<=20,1<=M<=1e5)

链接:https://ac.nowcoder.com/acm/contest/19483/B

 

分析

1.由于m较大,n较小,每次询问中,如果以O(1)的时间复杂度答案才能很快算出。

2.每次询问对之后的询问没有影响。

考虑预处理(前缀和)。

 

注意维数较高时不写成dp[][][][][]...的形式,而使用状态压缩  

细节都在注释里

前缀和(未完待续)_i++前缀和(未完待续)_i++_02
 1 #include<bits/stdc++.h> // O(n 2^n) 
 2 #define  ll long long
 3 using namespace std;
 4 const int N = 5000004;
 5 int n,m,k,x;
 6 int a[N];
 7 ll pre[N],suf[N];
 8 int maxx;
 9 
10 void init(){
11     maxx=(1<<n);
12     for(int i=0;i<maxx;i++){   //状态压缩 :表示所有物品取或者不取的一个总状态 
13         int tem=0;
14         for(int j=0;j<n;j++){  //枚举第j件物品 
15             if(i&(1<<j)) tem=(tem^a[j]);  //当前状态是否取了物品j 
16         }
17         pre[i]=tem;
18         suf[i]=tem;
19     }
20     for(int j=0;j<n;j++){
21         for(int i=0;i<maxx;i++){
22             if(i&(1<<j)) pre[i]+=pre[i^(1<<j)];  //prefix 子集和  还可能含有U中没有的元素 
23             else suf[i]+=suf[i^(1<<j)];   //suffix 超集和  还可能含有U中有的元素 
24         }
25     }
26 }
27 
28 int main(){
29     cin>>n>>m;
30     for(int i=0;i<n;i++) cin>>a[i];
31     init();
32     while(m--){
33         cin>>k;
34         if(k==0){
35             cout<<pre[0]<<" "<<suf[0]<<endl;
36             continue;
37         }
38         int tem=0;
39         for(int i=1,x;i<=k;i++){
40             cin>>x;
41             tem=(tem|(1<<(x-1)));
42         }
43         cout<<pre[tem]<<" "<<suf[tem]<<endl;
44     }
45     return 0;
46 }
View Code