1. 程式人生 > >【挖坑記】JZOJ 4738 神在夏至祭降下了神諭

【挖坑記】JZOJ 4738 神在夏至祭降下了神諭

題目大意

一個長度為n的01序列要分段,每一段的0、1個數不能相差k以上,問有多少種分段方案。
n<=1e5,k<=n
時間限制 1s
空間限制 256M

解題思路

n^2的DP很容易得出來,在此基礎上優化。
b[i]表示前1~i個數中0、1個數差,則f[i]=sigma(f[j]) (|b[j]-b[i]<=k|)。
建一棵線段樹,表示b[i]這個位置上f[i]的和,無腦查詢即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100006
#define fr(i,a,b) for(i=a;i<=b;i++)
using namespace std; typedef long long ll; const ll ding=1e9+7; int i,n,k,a[maxn],b[maxn]; ll s,f[maxn],tr[maxn*8]; void modify(int v,int st,int en,int x,int y) { if (st==en) { tr[v]=(tr[v]+y)%ding; return; } int m=(st+en) >> 1; if (x<=m) modify(v+v,st,m
,x,y); else modify(v+v+1,m+1,en,x,y); tr[v]=(tr[v+v]+tr[v+v+1])%ding; return; } void findd(int v,int st,int en,int l,int r) { if (st==l && en==r) { s=(s+tr[v])%ding; return; } int m=(st+en) >> 1; if (r<=m) findd(v+v,st,m,l,r); else
if (l>m) findd(v+v+1,m+1,en,l,r); else { findd(v+v,st,m,l,m); findd(v+v+1,m+1,en,m+1,r); } return; } int main() { scanf("%d%d",&n,&k); fr(i,1,n) { scanf("%d",&a[i]); if (a[i]) b[i]=b[i-1]+1; else b[i]=b[i-1]-1; } f[0]=1; modify(1,0,n+n,n,1); fr(i,1,n) { int t=b[i]+n; s=0; findd(1,0,n+n,max(0,t-k),min(t+k,n+n)); f[i]=s; modify(1,0,n+n,t,f[i]); } printf("%lld\n",f[n]); return 0; }