牛客網 NOIP賽前集訓營-普及組(第四場)C--部分和 (高維字首和)
阿新 • • 發佈:2018-11-22
解題思路
高維字首和模板題。首先,求字首和有兩種方式,比如說對於求二維字首和來說。
第一種 :
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
這一種其實就相當於用了容斥原理。
第二種 :
for(int i=1;i<=n;i++) sum[i][j]+=sum[i-1][j];
for(int i=1;i<=n;i++) sum[i][j]+=sum[i][j-1];
這一種其實相當於先求出列的字首和,再求行的字首和。
二維的時候這兩種似乎看起來差不多,如果擴充套件到三維?
第一種 :
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) for(int k=1;k<=n;k++){ sum[i][j][k]=sum[i-1][j][k]+sum[i][j-1][k]+sum[i][j][k-1]; sum[i][j][k]-=sum[i-1][j-1][k]+sum[i-1][j][k-1]+sum[i][j-1][k-1]; sum[i][j][k]+=sum[i-1][j-1][k-1]; }
這樣就有點麻煩了,再來看看第二種。
第二種 :
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) for(int k=1;k<=n;k++) sum[i][j][k]+=sum[i][j][k-1]; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) for(int k=1;k<=n;k++) sum[i][j][k]+=sum[i][j-1][k]; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) for(int k=1;k<=n;k++) sum[i][j][k]+=sum[i-1][j][k];
這樣就比較舒服了。
隨著維度的不斷增加,第一種方法需要容斥,這個複雜度就達到了 \(2^t\),\(t\)表示維度。那麼總的時間複雜度就是\(O(2^t*n^t)\),第二種的複雜度則是\(O(t*n^t)\),優秀很多。利用這個玩意,就可以算出高維字首和,高維字首和一般都是\(n=2\)的情況,一般來說就是求一個集合的超集或子集這類的東西。
對於這道題來說其實就是一個求子集的和。
程式碼
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
const int MAXN = (1<<20)+5;
inline int rd(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {f=ch=='-'?0:1;ch=getchar();}
while(isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return f?x:-x;
}
int n,a[MAXN];
int main(){
n=rd();for(int i=0;i<n;i++) a[i]=rd();int len=log2(n);
for(int i=1;i<=len;i++)
for(int j=0;j<n;j++)
if((j&(1<<(i-1)))) a[j]+=a[j^(1<<(i-1))];
for(int i=0;i<n;i++) printf("%d\n",a[i]);
return 0;
}