BZOJ 1049 [HAOI2006]数字序列 DP
第一问:最长xx子序列
第二问:f[i]表示前i个的最少花费,可以从满足条件j(假设以a[i]结尾的最长xx组序列长度为len,则j需要满足以a[j]结尾的最长xx组序列长度为len-1)
方法:记录最长xx子序列的转移,邻接表存,然后枚举每个转移。
ps:有个结论:如果从j转移到i的话,那么中间一定有一个k(k>=j&&k<i),使得j~k的高度都是a[j],k+1~i的高度都是i,且这样的花费是最优的,很容易想明白的~
这个暴力显然tle,怎么可能会ac?
数据弱,不解释
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cstdio> 5 #include <algorithm> 6 7 #define N 55555 8 #define INF 1LL<<60 9 10 using namespace std; 11 12 int a[N],c[N]; 13 int n,cnt,len; 14 int dp[N]; 15 int head[N],next[N],to[N]; 16 long long f[N],sum1[N],sum2[N]; 17 18 inline void add(int u,int v) 19 { 20 to[cnt]=v; next[cnt]=head[u]; head[u]=cnt++; 21 } 22 23 inline void read() 24 { 25 scanf("%d",&n); 26 for(int i=1;i<=n;i++) 27 { 28 scanf("%d",&a[i]); 29 a[i]-=i; 30 } 31 } 32 33 inline void step1() 34 { 35 n++; 36 a[n]=0x3f3f3f3f; a[0]=-a[n]; 37 for(int i=0;i<=n;i++) c[i]=0x3f3f3f3f; 38 len=1; c[1]=a[1]; c[0]=-c[0]; 39 dp[0]=0; dp[1]=1; 40 for(int i=2;i<=n;i++) 41 { 42 int x=upper_bound(c,c+1+len,a[i])-c; 43 len=max(len,x); 44 c[x]=min(c[x],a[i]); 45 dp[i]=x; 46 } 47 cout<<n-dp[n]<<endl; 48 } 49 50 inline void step2() 51 { 52 memset(head,-1,sizeof head); cnt=0; 53 for(int i=n;i>=0;i--) add(dp[i],i),f[i]=INF; 54 f[0]=0; 55 for(int i=1,tmp;i<=n;i++) 56 for(int j=head[dp[i]-1];~j;j=next[j]) 57 { 58 if(to[j]>i) break; 59 if(a[to[j]]>a[i]) continue; 60 for(int k=to[j];k<=i;k++) sum1[k]=abs(a[k]-a[to[j]]),sum2[k]=abs(a[k]-a[i]); 61 for(int k=to[j]+1;k<=i;k++) sum1[k]+=sum1[k-1],sum2[k]+=sum2[k-1]; 62 for(int k=to[j];k<i;k++) 63 f[i]=min(f[i],f[to[j]]+sum1[k]-sum1[to[j]]+sum2[i]-sum2[k]); 64 } 65 cout<<f[n]<<endl; 66 } 67 68 inline void go() 69 { 70 step1(); 71 step2(); 72 } 73 74 int main() 75 { 76 read(),go(); 77 return 0; 78 }
没有人能阻止我前进的步伐,除了我自己!