1. 程式人生 > >Usaco Training Section 4.3 Buy Low, Buy Lower

Usaco Training Section 4.3 Buy Low, Buy Lower

又遇到了多年不見的高精度!!!

題目很好懂,求最長下降子序列的長度及個數(相同的算一個)

一開始很激動,樣例都沒看,以為只求最長下降子序列的長度。直接把序列倒過來,跑一個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的個數。而且這題要一次做對也不簡單,高精度……真的容易被忽略。