[Poi2010]Monotonicity 2
题目
给出N个正整数a[1..N],再给出K个关系符号(>、<或=)s[1..k]。选出一个长度为L的子序列(不要求连续),要求这个子序列的第i项和第i+1项的的大小关系为s[(i-1)mod K+1]。求出L的最大值。INPUT
第一行两个正整数,分别表示N和K (N, K <= 500,000)。第二行给出N个正整数,第i个正整数表示a[i] (a[i] <= 10^6)。第三行给出K个空格隔开关系符号(>、<或=),第i个表示s[i]。OUTPUT
一个正整数,表示L的最大值。SAMPLE
INPUT
7 32 4 3 1 3 5 3< > =OUTPUT
6
解题报告
考试时连最最最简单的DP都没想出来,就打了个DFS- -
正解:
我们先考虑只有一种符号的情况,比如说考虑<,那么不就变成了求最长上升子序列吗。
同样的,我们扩展至三种符号:
1 for(int i=1;i<=n;i++){ 2 f[i]=1; 3 for(int j=1;j<=i-1;j++){ 4 int tmp(f[j]%k+1); 5 if(op[tmp]=='='&&a[j]==a[i]&&f[j]+1>f[i]) 6 f[i]=f[j]+1; 7 if(op[tmp]=='>'&&a[j]>a[i]&&f[j]+1>f[i]) 8 f[i]=f[j]+1; 9 if(op[tmp]=='<'&&a[j]<a[i]&&f[j]+1>f[i]) 10 f[i]=f[j]+1; 11 } 12 }
这就是最最最简单的DP,然而我们知道,这玩意是O(n²)的复杂度,显然会T,那么我们就需要优化一下了。
我们发现,转移时有O(n)的复杂度来找最大值,那么我们想,是否可以把这个过程优化呢?自然可以,我们的目的在于找到权值符合条件的最大f值,所以,我们需要一个新的东西来完成它:
权值线段树
这是一个神奇的数据结构- -,好吧,也不怎么神奇,它在这道题里是以权值为下标,存入该点最优解的一种线段树,它就可以完成这个伟大的任务啦。
我们需要3棵树(其实2棵也可以,相等的那个用数组模拟即可实现,只是我比较懒- -),每一棵树存以该符号为后面所接符号的权值的最优解(好绕啊- -),这样我们在找的时候,取出每棵树中符合权值条件的最优解,三解进行比较,选出最优以确定符号,继续转移并更新相应的线段树即可。
(我语文表达能力好弱啊)
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 inline int read(){ 6 int sum(0); 7 char ch(getchar()); 8 for(;ch<'0'||ch>'9';ch=getchar()); 9 for(;ch>='0'&&ch<='9';sum=sum*10+(ch^48),ch=getchar()); 10 return sum; 11 } 12 inline char init(){ 13 char ch(getchar()); 14 for(;ch!='='&&ch!='>'&&ch!='<';ch=getchar()); 15 return ch; 16 } 17 inline int my_max(int a,int b){ 18 return a>b?a:b; 19 } 20 int n,k; 21 int a[500001],op[500001]; 22 int tr_d[4000001],tr_x[4000001],tr_e[4000001]; 23 int ad_d[4000001],ad_x[4000001],ad_e[4000001]; 24 inline void pushup_d(int i){ 25 tr_d[i]=my_max(tr_d[i<<1],tr_d[i<<1|1]); 26 } 27 inline void pushup_x(int i){ 28 tr_x[i]=my_max(tr_x[i<<1],tr_x[i<<1|1]); 29 } 30 inline void pushup_e(int i){ 31 tr_e[i]=my_max(tr_e[i<<1],tr_e[i<<1|1]); 32 } 33 inline void pushdown_d(int i){ 34 if(ad_d[i]){ 35 ad_d[i<<1]=ad_d[i]; 36 ad_d[i<<1|1]=ad_d[i]; 37 tr_d[i<<1]=ad_d[i]; 38 tr_d[i<<1|1]=ad_d[i]; 39 tr_d[i]=ad_d[i]; 40 ad_d[i]=0; 41 } 42 } 43 inline void pushdown_x(int i){ 44 if(ad_x[i]){ 45 ad_x[i<<1]=ad_x[i]; 46 ad_x[i<<1|1]=ad_x[i]; 47 tr_x[i<<1]=ad_x[i]; 48 tr_x[i<<1|1]=ad_x[i]; 49 tr_x[i]=ad_x[i]; 50 ad_x[i]=0; 51 } 52 } 53 inline void pushdown_e(int i){ 54 if(ad_e[i]){ 55 ad_e[i<<1]=ad_e[i]; 56 ad_e[i<<1|1]=ad_e[i]; 57 tr_e[i<<1]=ad_e[i]; 58 tr_e[i<<1|1]=ad_e[i]; 59 tr_e[i]=ad_e[i]; 60 ad_e[i]=0; 61 } 62 } 63 inline void update_d(int ll,int rr,int c,int l,int r,int i){ 64 if(ll<=l&&r<=rr){ 65 ad_d[i]=c; 66 tr_d[i]=c; 67 return; 68 } 69 pushdown_d(i); 70 int mid((l+r)>>1); 71 if(ll<=mid) 72 update_d(ll,rr,c,l,mid,i<<1); 73 if(rr>mid) 74 update_d(ll,rr,c,mid+1,r,i<<1|1); 75 pushup_d(i); 76 } 77 inline void update_x(int ll,int rr,int c,int l,int r,int i){ 78 if(ll<=l&&r<=rr){ 79 ad_x[i]=c; 80 tr_x[i]=c; 81 return; 82 } 83 pushdown_x(i); 84 int mid((l+r)>>1); 85 if(ll<=mid) 86 update_x(ll,rr,c,l,mid,i<<1); 87 if(rr>mid) 88 update_x(ll,rr,c,mid+1,r,i<<1|1); 89 pushup_x(i); 90 } 91 inline void update_e(int ll,int rr,int c,int l,int r,int i){ 92 if(ll<=l&&r<=rr){ 93 ad_e[i]=c; 94 tr_e[i]=c; 95 return; 96 } 97 pushdown_e(i); 98 int mid((l+r)>>1); 99 if(ll<=mid) 100 update_e(ll,rr,c,l,mid,i<<1); 101 if(rr>mid) 102 update_e(ll,rr,c,mid+1,r,i<<1|1); 103 pushup_e(i); 104 } 105 inline int query_d(int ll,int rr,int l,int r,int i){ 106 if(ll>rr) 107 return 0; 108 if(ll<=l&&r<=rr) 109 return tr_d[i]; 110 pushdown_d(i); 111 int mid((l+r)>>1); 112 int ret(0); 113 if(ll<=mid) 114 ret=my_max(ret,query_d(ll,rr,l,mid,i<<1)); 115 if(rr>mid) 116 ret=my_max(ret,query_d(ll,rr,mid+1,r,i<<1|1)); 117 return ret; 118 } 119 inline int query_x(int ll,int rr,int l,int r,int i){ 120 if(ll>rr) 121 return 0; 122 if(ll<=l&&r<=rr) 123 return tr_x[i]; 124 pushdown_x(i); 125 int mid((l+r)>>1); 126 int ret(0); 127 if(ll<=mid) 128 ret=my_max(ret,query_x(ll,rr,l,mid,i<<1)); 129 if(rr>mid) 130 ret=my_max(ret,query_x(ll,rr,mid+1,r,i<<1|1)); 131 return ret; 132 } 133 inline int query_e(int ll,int rr,int l,int r,int i){ 134 if(ll>rr) 135 return 0; 136 if(ll<=l&&r<=rr) 137 return tr_e[i]; 138 pushdown_e(i); 139 int mid((l+r)>>1); 140 int ret(0); 141 if(ll<=mid) 142 ret=my_max(ret,query_e(ll,rr,l,mid,i<<1)); 143 if(rr>mid) 144 ret=my_max(ret,query_e(ll,rr,mid+1,r,i<<1|1)); 145 return ret; 146 } 147 int f[500001]; 148 int mx(0); 149 int main(){ 150 n=read(),k=read(); 151 for(int i=1;i<=n;i++) 152 a[i]=read(),mx=my_max(mx,a[i]); 153 for(int i=1;i<=k;i++){ 154 char ch(init()); 155 if(ch=='>') 156 op[i]=1; 157 if(ch=='<') 158 op[i]=2; 159 if(ch=='=') 160 op[i]=3; 161 } 162 f[1]=1; 163 if(op[1]==1) 164 update_d(a[1],a[1],f[1],1,mx,1); 165 if(op[1]==2) 166 update_x(a[1],a[1],f[1],1,mx,1); 167 if(op[1]==3) 168 update_e(a[1],a[1],f[1],1,mx,1); 169 for(int i=2;i<=n;i++){ 170 int now(a[i]); 171 int ans_d(query_d(now+1,mx,1,mx,1)); 172 int ans_x(query_x(1,now-1,1,mx,1)); 173 int ans_e(query_e(now,now,1,mx,1)); 174 int ans(my_max(my_max(ans_d,ans_x),ans_e)); 175 f[i]=ans+1; 176 int o(op[ans%k+1]);//cout<<i<<' '<<f[i]<<' '<<o<<endl; 177 if(o==1) 178 update_d(now,now,f[i],1,mx,1); 179 if(o==2) 180 update_x(now,now,f[i],1,mx,1); 181 if(o==3) 182 update_e(now,now,f[i],1,mx,1); 183 } 184 int mxx(0); 185 for(int i=1;i<=n;i++) 186 mxx=my_max(mxx,f[i]); 187 printf("%d",mxx); 188 }
写的极其丑- -,毕竟三颗线段树乱搞
凑合着看吧,其实理解了之后,一颗线段树,加不同的域,对传的参数进行处理,就可以达到三颗线段树的效果