BZOJ3594 SCOI2014方伯伯的玉米田(动态规划+树状数组)
可以发现每次都对后缀+1是不会劣的。考虑dp:设f[i][j]为前i个数一共+1了j次时包含第i个数的LIS长度。则f[i][j]=max(f[i][j-1],f[k][l]+1) (k<i,l<=j,a[i]+j>=a[k]+l)。容易发现这里是二维偏序,相当于查询(j,a[i]+j)左下部分的最大值,二维树状数组暴力维护,复杂度O(nklogklogv)。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 10010 #define M 510 #define V 5010 int n,m,a[N],f[N][M]; int tree[M][V+M]; void ins(int x,int y,int k) { for (;x<=m+1;x+=x&-x) for (int i=y;i<=5500;i+=i&-i) tree[x][i]=max(tree[x][i],k); } int query(int x,int y) { int s=0; for (;x;x-=x&-x) for (int i=y;i;i-=i&-i) s=max(s,tree[x][i]); return s; } int main() { #ifndef ONLINE_JUDGE freopen("bzoj3594.in","r",stdin); freopen("bzoj3594.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(),m=read(); int t=0; for (int i=1;i<=n;i++) a[i]=read(); for (int i=1;i<=n;i++) { for (int j=0;j<=m;j++) { if (j) f[i][j]=f[i][j-1]; f[i][j]=max(f[i][j],query(j+1,a[i]+j)+1); } for (int j=0;j<=m;j++) ins(j+1,a[i]+j,f[i][j]); } for (int i=1;i<=n;i++) f[0][m]=max(f[0][m],f[i][m]); cout<<f[0][m]; return 0; }
虽然看到题解清一色的都是二维BIT,仔细琢磨一下还是感觉自己原来的维护方法并没有假。容易发现f[i][j]>=f[i][j-1],那么k确定时l取值越大越好,所以两个限制至少有一个取等号。那么可以改为维护很多棵BIT,复杂度变为O(nk(logk+logv))。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 10010 #define M 510 #define V 5010 int n,m,a[N],f[N][M]; int tree1[M][V+M],tree2[V+M][M]; void ins1(int x,int k,int p){while (k<=5500) tree1[x][k]=max(tree1[x][k],p),k+=k&-k;} void ins2(int x,int k,int p){while (k<=501) tree2[x][k]=max(tree2[x][k],p),k+=k&-k;} int query1(int x,int k){int s=0;while (k) s=max(s,tree1[x][k]),k-=k&-k;return s;} int query2(int x,int k){int s=0;while (k) s=max(s,tree2[x][k]),k-=k&-k;return s;} int main() { #ifndef ONLINE_JUDGE freopen("bzoj3594.in","r",stdin); freopen("bzoj3594.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(),m=read(); int t=0; for (int i=1;i<=n;i++) a[i]=read(); for (int i=1;i<=n;i++) { for (int j=0;j<=m;j++) { if (j) f[i][j]=f[i][j-1]; f[i][j]=max(f[i][j],max(query1(j,a[i]+j),query2(a[i]+j,j+1))+1); } for (int j=0;j<=m;j++) ins1(j,a[i]+j,f[i][j]),ins2(a[i]+j,j+1,f[i][j]); } for (int i=1;i<=n;i++) f[0][m]=max(f[0][m],f[i][m]); cout<<f[0][m]; return 0; }