首页 > 代码库 > [Poi2010]Monotonicity 2 (线段树优化DP)

[Poi2010]Monotonicity 2 (线段树优化DP)

题目描述

给出N个正整数a[1..N],再给出K个关系符号(>、<或=)s[1..k]。
选出一个长度为L的子序列(不要求连续),要求这个子序列的第i项和第i+1项的的大小关系为s[(i-1)mod K+1]。
求出L的最大值。

输入

第一行两个正整数,分别表示N和K (N, K <= 500,000)。
第二行给出N个正整数,第i个正整数表示a[i] (a[i] <= 10^6)。
第三行给出K个空格隔开关系符号(>、<或=),第i个表示s[i]。

输出

一个正整数,表示L的最大值。

样例输入

7 3
2 4 3 1 3 5 3
< > =

样例输出

6


提示

选出的子序列为2 4 3 3 5 3,相邻大小关系分别是< > = < >。

 1 #include<cmath>
 2 #include<ctime>
 3 #include<cstdio>
 4 #include<cstdlib>
 5 #include<cstring>
 6 #include<iostream>
 7 #include<algorithm>
 8 # define maxn 500010
 9 # define lson rt<<1,l,mid
10 # define rson rt<<1|1,mid+1,r
11 using namespace std;
12 int n,K;
13 int a[maxn];
14 int hao[maxn],ord[maxn];
15 int lim;
16 void init(){
17     scanf("%d%d",&n,&K);
18     for(int i=1;i<=n;i++) scanf("%d",&a[i]),lim=max(lim,a[i]);
19     char s[2];
20     for(int i=1;i<=K;i++){
21         scanf("%s",&s);
22         if(s[0]==<) hao[i]=1;
23         if(s[0]===) hao[i]=2;
24         if(s[0]==>) hao[i]=3;
25     }
26     for(int i=1;i<=n;i++)
27         ord[i]=hao[(i-1)%K+1];
28 }
29 int f[maxn];
30 struct XDS{
31     int mx[4000010];
32     void add(int now,int w,int rt,int l,int r){
33         //cout<<now<<"  "<<w<<"  "<<rt<<"  "<<l<<"  "<<r<<endl;
34         if(l==r){
35             mx[rt]=max(mx[rt],w);
36             return;
37         }
38         int mid=(l+r)>>1;
39         if(now<=mid){
40             add(now,w,lson);
41         }
42         if(now>mid){
43             add(now,w,rson);
44         }
45         mx[rt]=max(mx[rt<<1],mx[rt<<1|1]);  
46     }
47     int find(int left,int right,int rt,int l,int r){
48         if(left<=l && r<=right){
49             return mx[rt];
50         }
51         int mid=(l+r)>>1;
52         int ans=0;
53         if(left<=mid){
54             ans=max(ans,find(left,right,lson));
55         }
56         if(right>mid){
57             ans=max(ans,find(left,right,rson));
58         }
59         return ans;
60     }
61 };
62 XDS da,xiao;
63 int deng[1000010];
64 void fang(int now,int w,int od){
65 //  cout<<"now== "<<now<<"  "<<w<<" "<<od<<endl;
66     if(od==1){
67         da.add(now,w,1,0,lim);
68     }
69     if(od==3){
70         xiao.add(now,w,1,0,lim);
71     }
72     if(od==2){
73         deng[now]=max(deng[now],w);
74     }
75 }
76 int main(){
77     //freopen("a.in","r",stdin);
78     //freopen("mot.in","r",stdin); freopen("mot.out","w",stdout);
79     init();
80     for(int i=1;i<=n;i++){
81         int x;
82         x=deng[a[i]]+1;
83         fang(a[i],x,ord[x]);
84         x=da.find(0,a[i]-1,1,0,lim)+1;
85         fang(a[i],x,ord[x]);
86         x=xiao.find(a[i]+1,lim,1,0,lim)+1;
87         fang(a[i],x,ord[x]);
88     }
89     int ans=0;
90     for(int i=0;i<=lim;i++){
91         ans=max(deng[i],ans);
92     }
93     ans=max(ans,da.find(0,lim,1,0,lim));
94     ans=max(ans,xiao.find(0,lim,1,0,lim));
95     cout<<ans<<endl;
96     return 0;
97       
98 }

 

 

这道题是一道DP,如果是同 f[i][k]=max(f[j-1][t]+1); 其中i表示位置,k表示符号,但是n^2的效率肯定过不了,所以用线段树优化,

造两棵线段树,都按a[i]为小标,一个是下一个为大于号的,一个是下一个为小于号的,维护在a[i]且符号一定是的f[i]最大值,至于等于号直接开一个数组就行了,在转移的时候,例如要找能放大于号的,那么就直接在对应的书中在[1,a[i]-1]找最大值就行了,再将新更新出来的插入对应的树种,但是在插入的时候,一定要保证先插入等于的,因为如果先插入大于或小于的,可能会更新出下一个符号是等于号的,但是在插入后,因为等于号a[i]就是从a[i]转移,所以可能将原来的a[i]处的f[i]更新,导致答案变大,但是大于小于号不用管顺序,因为他们不从a[i]转移

 

 

[Poi2010]Monotonicity 2 (线段树优化DP)