Usaco Training Section 4.3 Buy Low, Buy Lower
阿新 • • 發佈:2018-11-12
又遇到了多年不見的高精度!!!
題目很好懂,求最長下降子序列的長度及個數(相同的算一個)
一開始很激動,樣例都沒看,以為只求最長下降子序列的長度。直接把序列倒過來,跑一個lis,O(nlogn)。後來發現還要求個數,崩潰*1。很快發現其實還好,只不過好像個數只能O(n^2)來求。還有個頭疼的問題,相同的只能算一個,於是我開始亂搞,用map+set,寫了好長,崩潰*2,一交,NO!!!還要高精度!!!崩潰*3。加了個高精度(因為我的寫法比較奇怪,涉及了高精加和高精減),一交,TLE!崩潰*4。於是想開n個樹狀陣列,但發現貌似會MLE,因為USACO Training的記憶體好像只給了16M。崩潰*5。於是我想了好久。突然發現,對於f[i]和a[i]都相等的數可以直接忽略(a[i]表示第i個數的值,f[i]表示到a[i]為止的LIS長度)。這下就好做了。於是……過了。
法一:n^2lis+n^2dp
#include<bits/stdc++.h> #define ull unsigned long long #define inf 2147483647 #define mp make_pair #define pii pair<int,int> #define pb push_back using namespace std; inline int read(){ int x=0;char c=getchar(); while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x; } int a[5005],f[5005]; string ans[5005]; int c[305],b[305]; inline string add(string s1,string s2){ int n=s1.length(),m=s2.length(); if(n<m) swap(n,m),swap(s1,s2); for(int i=n;i;--i) c[i]=s1[n-i]-48; for(int i=m;i;--i) b[i]=s2[m-i]-48; for(int i=m+1;i<=n;++i) b[i]=0; c[n+1]=0; for(int i=1;i<=n;++i) c[i+1]+=(c[i]+b[i])/10,c[i]=(c[i]+b[i])%10; if(c[n+1]>0) ++n; string s=""; for(int i=n;i;--i) s+=c[i]+48; return s; } int main() { freopen("buylow.in","r",stdin); freopen("buylow.out","w",stdout); int n=read(); for(int i=n;i;--i) a[i]=read(),ans[i]="0"; a[++n]=inf; ans[0]="1"; for(int i=1;i<=n;++i){ for(int j=0;j<i;++j) if(a[i]>a[j]&&f[j]+1>f[i]) f[i]=f[j]+1; for(int j=i-1;j>=0;--j){ if(f[j]==f[i]&&a[j]==a[i]) break; if(f[j]+1==f[i]&&a[j]<a[i]) ans[i]=add(ans[i],ans[j]); } } cout<<f[n]-1<<' '<<ans[n]<<'\n'; }
法二:nlogn lis+n^2dp
#include<bits/stdc++.h> #define ull unsigned long long #define inf 2147483647 #define mp make_pair #define pii pair<int,int> #define pb push_back using namespace std; inline int read(){ int x=0;char c=getchar(); while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x; } int a[5005],f[5005],dp[5005]; string ans[5005]; int c[305],b[305]; inline string add(string s1,string s2){ int n=s1.length(),m=s2.length(); if(n<m) swap(n,m),swap(s1,s2); for(int i=n;i;--i) c[i]=s1[n-i]-48; for(int i=m;i;--i) b[i]=s2[m-i]-48; for(int i=m+1;i<=n;++i) b[i]=0; c[n+1]=0; for(int i=1;i<=m;++i) c[i+1]+=(c[i]+b[i])/10,c[i]=(c[i]+b[i])%10; if(c[n+1]>0) ++n; string s=""; for(int i=n;i;--i) s+=c[i]+48; return s; } int main() { freopen("buylow.in","r",stdin); freopen("buylow.out","w",stdout); int n=read(),m=0; for(int i=n;i;--i) a[i]=read(),ans[i]="0"; for(int i=1;i<=n;++i){ int x=lower_bound(dp+1,dp+m+1,a[i])-dp; dp[x]=a[i]; f[i]=x; x>m?m=x:x=x; } ans[0]="1"; for(int i=1;i<=n;++i){ for(int j=i-1;j>=0;--j){ if(f[j]==f[i]&&a[j]==a[i]) break; if(f[j]+1==f[i]&&a[j]<a[i]) ans[i]=add(ans[i],ans[j]); } if(f[i]==m) ans[n+1]=add(ans[n+1],ans[i]); } cout<<m<<' '<<ans[n+1]<<'\n'; }
發現usaco training的section4裡的題目難度明顯有了提升。這題也挺有質量的。以前從沒見過求LIS的個數。而且這題要一次做對也不簡單,高精度……真的容易被忽略。