题解 P1311 【选择客栈】

· · 题解

主要是给自己复习用的...

首先,看到题目,你首先想的是什么呢?

虽然我不知道你想的是什么,但是我想的就是一个区间最值查询问题(RMQ),所以我写了ST表,如下是代码:

#include<cstdio>
inline int read(){
    int r=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){r=(r<<3)+(r<<1)+(c-48);c=getchar();}
    return r*f;
}
int f[21][200001],Log[200001],mi[30],a[200001],c[200001],q[50][200001];
inline int min(int a,int b){
    return a<b?a:b;
}
inline void init(int n){//初始化ST表
    int i,j;
    Log[0]=-1;
    for(i=1;i<=n;i++)Log[i]=Log[i/2]+1;
    mi[0]=1;
    for(i=1;i<=30;i++)mi[i]=2*mi[i-1];
    for(i=1;i<=n;i++)f[0][i]=a[i];
    for(i=1;i<=Log[n];i++)
        for(j=1;j+mi[i]-1<=n;j++)
            f[i][j]=min(f[i-1][j],f[i-1][j+mi[i-1]]);
}
inline int query(int l,int r){//查询
    int k=Log[r-l+1];
    return min(f[k][l],f[k][r-mi[k]+1]);
}
int main(){
    int n=read(),K=read(),p=read(),i,j,k,ans=0;
    for(i=1;i<=n;i++){
        c[i]=read();
        a[i]=read();
        q[c[i]][++q[c[i]][0]]=i;
    }
    init(n);
    for(i=0;i<K;i++)//暴力枚举...
        for(j=1;j<q[i][0];j++)
            for(k=j+1;k<=q[i][0];k++)
                if(query(q[i][j],q[i][k])<=p)ans++;
    printf("%d",ans);
    return 0;
}

果断提交,然而3个TLE...

这时我就想,如何优化呢?

因为如果query(i,j)<=p那么query(i,j+1)(j<n)肯定也行!

所以代码就变成这样:

#include<cstdio>
inline int read(){
    int r=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){r=(r<<3)+(r<<1)+(c-48);c=getchar();}
    return r*f;
}
int f[21][200001],Log[200001],mi[31],a[200001],c[200001],q[50][200001];
inline int min(int a,int b){
    return a<b?a:b;
}
inline void init(int n){
    Log[0]=-1;
    for(register int i=1;i<=n;i++)Log[i]=Log[i/2]+1;
    mi[0]=1;
    for(register int i=1;i<=30;i++)mi[i]=2*mi[i-1];
    for(register int i=1;i<=n;i++)f[0][i]=a[i];
    for(register int i=1;i<=Log[n];i++)
        for(register int j=1;j+mi[i]-1<=n;j++)
            f[i][j]=min(f[i-1][j],f[i-1][j+mi[i-1]]);
}
inline int query(int l,int r){
    int k=Log[r-l+1];
    return min(f[k][l],f[k][r-mi[k]+1]);
}
int main(){
    int n=read(),K=read(),p=read(),b,ans=0,ans1=0;
    for(register int i=1;i<=n;i++){
        c[i]=read();
        a[i]=read();
        q[c[i]][++q[c[i]][0]]=i;
    }
    init(n);
    for(register int i=0;i<K;i++){
        b=2;
        ans1=ans;
        for(register int j=1;j<q[i][0];j++)
            for(register int k=b;k<=q[i][0];k++)
                if(query(q[i][j],q[i][k])<=p){
                    ans+=q[i][0]-k+1;//可以就一次加
                    b=k;//记录位置
                    break;
                }
            if(ans1==ans)break;//如果不行了,就换颜色
        }
    printf("%d",ans);
    return 0;
}

然而更奇怪的,变得只有一个点AC了。

突然看到当b==j+1(b!=2)时,下一次枚举就重复,所以经过修改,终于AC了!

AC代码:

#include<cstdio>
inline int read(){
    int r=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){r=(r<<3)+(r<<1)+(c-48);c=getchar();}
    return r*f;
}
int f[21][200001],Log[200001],mi[31],a[200001],c[200001],q[50][200001];
inline int min(int a,int b){
    return a<b?a:b;
}
inline void init(int n){
    Log[0]=-1;
    for(register int i=1;i<=n;i++)Log[i]=Log[i/2]+1;
    mi[0]=1;
    for(register int i=1;i<=30;i++)mi[i]=2*mi[i-1];
    for(register int i=1;i<=n;i++)f[0][i]=a[i];
    for(register int i=1;i<=Log[n];i++)
        for(register int j=1;j+mi[i]-1<=n;j++)
            f[i][j]=min(f[i-1][j],f[i-1][j+mi[i-1]]);
}
inline int query(int l,int r){
    int k=Log[r-l+1];
    return min(f[k][l],f[k][r-mi[k]+1]);
}
int main(){
    freopen("1.in","r",stdin);
    int n=read(),K=read(),p=read(),b,ans=0,ans1=0;
    for(register int i=1;i<=n;i++){
        c[i]=read();
        a[i]=read();
        q[c[i]][++q[c[i]][0]]=i;
    }
    init(n);
    for(register int i=0;i<K;i++){
        b=2;
        ans1=ans;
        for(register int j=1;j<q[i][0];j++){
            for(register int k=b;k<=q[i][0];k++)
                if(query(q[i][j],q[i][k])<=p){
                    ans+=q[i][0]-k+1;
                    b=k;
                    break;
                }
            if(ans1==ans)break;
            if(b<=j+1)b=j+2;
        }
    }
    printf("%d",ans);
    return 0;
}

注意:千万不要抄袭啊!