Noip2011 提高组 选择客栈

P1311 选择客栈

直通

思路:

  ①看题,我们可以发现一个显然的性质,即当最左边的客栈向右移动时,最右边的客栈时单调向右的,并且右端点往右的客栈也符合要求。(因为只要左侧有一个满足的,右边的自然可以选)

  不妨将每种颜色的宾馆分别放到 vector 中。

  然后在每个 vector 中枚举左端点,维护一个单调指针来确定右端点 (vector中的下标)。

  我们接下来就要快速判断一段区间是否合法,我们开一个ok数组,表示从i点开始最近的满足条件的位置(不考虑颜色)。

    这样的话转移就是:

            ok[i]=i;(p[i]<=P),反之:ok[i]=ok[i+1];

            需要注意的是ok[n+1]=n+1;

            表示如果p[n]>P则p[n]=p[n+1]=n+1,即不合法

  ②大模拟,详细看代码

坑点:

  注意输入顺序qwq

上代码:

1)vector版

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;

const int K = 50;
const int N = 200001;
int n,k,p,ans;
int w[N],ok[N];
vector<int>v[K];

void getnxt() {
    ok[n+1]=n+1;
    for(int i=n; i>=1; i--) {
        if(w[i]<=p) ok[i]=i;
        else ok[i]=ok[i+1]; 
    }
}

int main() {
    scanf("%d%d%d",&n,&k,&p);
    for(int i=1,x; i<=n; i++) {
        scanf("%d%d",&x,&w[i]);
        v[x].push_back(i);
    }
    getnxt();
    for(int i=0,d,s; i<k; i++) {
        d=0;
        s=v[i].size(); //i客栈连接的s个客栈 
        for(int j=0; j<s; j++) {
            d=max(d,j+1); //当前枚举到的最右端的客栈所在色调的编号是多少 
            while(ok[v[i][j]]>v[i][d] && d<s) {
                d++;
            } //这里不太懂... 
            ans+=s-d;
        }
    }
    printf("%d",ans);
    return 0;
}
View Code

2)纯模拟版

#include <iostream>
#include <cstdio>
using namespace std;

const int K = 101;
int n,k,p,ans;
int precnt[K],ok[K],last[K];
//precnt[x] i点前颜色为x的客栈数
//ok[x]     i点前满足能有价格<=p的客栈的颜色为x的客栈数
//last[x]   i点 前一个颜色 为x的点的编号

int main() {
    scanf("%d%d%d",&n,&k,&p);
    for(int i=1,tmp,x,y; i<=n; i++) {
        scanf("%d%d",&x,&y);
        if(y<=p) tmp=i; //记录下来当前色调的可行的最右客栈编号 
        if(tmp>=last[x]) ok[x]=precnt[x];
        ans+=ok[x];
        precnt[x]++; //不管是不是可以满足条件 
        last[x]=i; 
    }
    printf("%d",ans);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/zxqxwnngztxx/p/7763997.html