bzoj3173 [Tjoi2013]最长上升子序列
这个题...被恶心到了。
如果插入的数是无序的话,貌似就要做三维偏序了= =,但这个题就不需要了哈。
其实,LIS是可以用bit做的,每次我们可以求出以当前插入的数为结尾的LIS长度,也就是查询x的前缀max,插入的时候把查询结果+1插入bit。我们看到,如果插入的数的递增的话,要么新的LIS是以x结尾,要么不变,于是扫一遍就好了。
但是我们得知道插入数的实际位置,怎么做?有一种比较简单的方法就是倒着扫序列,二分答案+bit,这个bit维护前缀和,也就是求比二分的位置小的数的个数,与插入位置相比。我一开始想的是直接用bit求比插入位置x小的数的个数,但这样是错的,因为插入这个数之前这个数的的位置有可能被顶到了后面,就不能单单用它插入的位置来查询。于是就应该用二分虚拟一个答案mid,通过ask(mid)与mid-x比较来确定实际位置。其实这样做就是替代了平衡树的作用。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #define maxn 220000 7 #define inf 1000000000 8 using namespace std; 9 int pos[maxn],a[maxn],f[maxn],size[maxn],c[maxn][2],fa[maxn]; 10 int n,m; 11 struct bit 12 { 13 int b[maxn]; 14 void add(int x,int z) 15 { 16 for (int i=x;i<=n;i+=(i&-i)) b[i]+=z; 17 } 18 int ask(int x) 19 { 20 int tmp=0; 21 for (int i=x;i;i-=(i&-i)) tmp+=b[i]; 22 return tmp; 23 } 24 }d; 25 struct bmx 26 { 27 int b[maxn]; 28 void add(int x,int z) 29 { 30 for (int i=x;i<=n;i+=(i&-i)) b[i]=max(b[i],z); 31 } 32 int ask(int x) 33 { 34 int tmp=0; 35 for (int i=x;i;i-=(i&-i)) tmp=max(b[i],tmp); 36 return tmp; 37 } 38 }s; 39 40 int main() 41 { 42 //freopen("sequence.in","r",stdin); 43 scanf("%d",&n); 44 for (int i=1;i<=n;i++) 45 scanf("%d",&a[i]),a[i]+=1; 46 for (int i=n;i;i--) 47 { 48 int l=1,r=n; 49 while (l<=r) 50 { 51 int mid=(l+r)>>1; 52 if (d.ask(mid)<=mid-a[i]) r=mid-1,pos[i]=mid; 53 else l=mid+1; 54 } 55 d.add(pos[i],1); 56 } 57 for (int i=1;i<=n;i++) 58 { 59 f[i]=s.ask(pos[i])+1; 60 s.add(pos[i],f[i]); 61 f[i]=max(f[i],f[i-1]); 62 } 63 for (int i=1;i<=n;i++) printf("%d\n",f[i]); 64 return 0; 65 }
AC without art, no better than WA !