做法: 设 d[i,j]表示第 i 个数到第 j 个数中 0 的个数减去 1 的个数。 设 f[i]表示第 1~i 个数分段的方案数。 则 f[i] = sigma(f[j]),|d[j+1,i]|≤K。 时间复杂度 O(N^2)。 考虑以下 i 个数: d[1,i] d[2,i] d[3,i] …… d[i,i] 我们发现,当 i 变成 i+1 时,这 i 个数要么一起+1,要么一起-1,而对于 i,要查询的 j 也就 是所有 d 的值在[-K,K]中的 f[j]。 所以可以考虑运动的相对性:把这 i 个数+1,看成查询区间向左移动一个单位,而-1 看成查询 区间向右移动一个单位。每当算出一个 f[i],就将 f[i]的值加进当前查询区间的中点即可。 可以使用线段树维护。 时间复杂度 O(N log N)
代码如下:
View Code
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #define mo 1000000007 5 #define LL long long 6 #define N 2000007 7 #define rep(i,a,b) for(int i=a;i<=b;i++) 8 using namespace std; 9 LL t[N],f[N/10],sum; 10 int n,k,b[N/10]; 11 12 void Update(int p,int l,int r,int ain,int x){ 13 if(l==r){ 14 t[p]=(t[p]+x)%mo; 15 return; 16 } 17 int mid=(l+r)>>1; 18 if(mid>=ain) Update(p<<1,l,mid,ain,x); 19 else Update(p<<1|1,mid+1,r,ain,x); 20 t[p]=(t[p<<1]+t[p<<1|1])%mo; 21 return; 22 } 23 24 void Find(int p,int l,int r,int L,int R){ 25 if(l==L&&r==R){ 26 sum=(sum+t[p])%mo; 27 return; 28 } 29 int mid=(l+r)>>1; 30 if(mid>=R) Find(p<<1,l,mid,L,R); 31 else if(mid<L) Find(p<<1|1,mid+1,r,L,R); 32 else{ 33 Find(p<<1,l,mid,L,mid); 34 Find(p<<1|1,mid+1,r,mid+1,R); 35 } 36 return; 37 } 38 39 void Init(){ 40 scanf("%d%d",&n,&k); 41 rep(i,1,n){ 42 int x; 43 scanf("%d",&x); 44 if(x) b[i]=b[i-1]+1; else b[i]=b[i-1]-1; 45 } 46 f[0]=1; 47 Update(1,0,2*n,n,1); 48 } 49 50 void Work(){ 51 rep(i,1,n){ 52 int site=b[i]+n; 53 sum=0; 54 Find(1,0,2*n,max(0,site-k),min(2*n,site+k)); 55 f[i]=sum; 56 Update(1,0,2*n,site,f[i]); 57 } 58 printf("%lld", f[n]); 59 } 60 61 int main(){ 62 Init(); 63 Work(); 64 }