[NOIp2011]选择客栈

 洛谷题号:P1311

出处: NOIp2011提高组 D1T2

主要算法:递推

难度:4.3

思路分析:

       先打了一个n^2暴力拿到了60分。简简单单的一个ST表RMQ预处理,然后n^2枚举客栈求区间最小值判一下是否<=p就可以了,还是很简单的。但是刚开始RMQ打错了。RMQ的边界条件还是非常坑的!

  说一说正解吧,正解跟RMQ压根没关系。

  我们把思路反过来(其实这是我一开始的思路):考虑每个满足<=p的客栈,在它两边的同种颜色的客栈都可以进行累计。那我们是不是可以枚举<=p的客栈往两边扩散呢?显然不行,因为这样子重复的选择情况太多了。如何避免重复呢?我们选择枚举右边的那个客栈,也就是区间的右边界r。找到在位置<=r的离他最近的那一个消费<=p的客栈v,这样位置小于v的同种颜色的客栈就全部可以与r匹配累计答案了。

  于是现在出现了两个问题:

  (一):如何求出对于每一个右边界i,离他最近的消费<=p的客栈的位置last[i]?注意不能枚举,因为n^2就爆了。所以必须要一个O(1)的算法。这个还是很容易的,只需要递推一下就行了。只有会去想都能够想到转移方程:如果当前这个客栈的消费就<=p,那么last[i] = i,否则选择继承,last[i] = last[i-1]

  (二):如何统计出在这个喝咖啡的客栈左侧的同种颜色的客栈个数?在读入时统计一个color[i][j]表示颜色为i的第j个客栈的位置。这个用二维数组不太方便,但用个vector是再好不过了的。这样,我们在统计选定的客栈左侧有多少个满足条件的合法客栈时,就可以二分——二分出颜色相同的在它左边的个数,答案累加。

  复杂度O(n log n)

代码注意点:

  有一个点非常坑,而且没注意到就是10分。那就是这两个人不会住到同一个客栈离去,这样的话如果二分出来的位置与右边界相同,就必须-1.

/*By QiXingzhi*/
#include <cstdio>
#include <vector>
#define  N  (200010)
#define  r  read()
#define  INF   (0x3f3f3f3f)
#define  Max(a,b)  (((a)>(b)) ? (a) : (b))
#define  Min(a,b)  (((a)<(b)) ? (a) : (b))
typedef long long ll;
using namespace std;
inline int read(){
    int x = 0; int w = 1; register int c = getchar();
    while(c ^ '-' && (c < '0' || c > '9')) c = getchar();
    if(c == '-') w = -1, c = getchar();
    while(c >= '0' && c <= '9') x = (x << 3) +(x << 1) + c - '0', c = getchar();
    return x * w;
}
int n,m,k,p,ans,x,L,R,Mid,_a;
int color[N],low[N],last[N];
vector <int> c[N];
int main(){
//    freopen(".in","r",stdin);
    n=r,k=r,p=r;
    for(int i = 1; i <= n; ++i){
        color[i] = r;
        low[i] = r;
        if(low[i] <= p) last[i] = i;
        else last[i] = last[i-1];
        c[color[i]].push_back(i);
    }
    for(int i = 2; i <= n; ++i){
        if(!last[i]) continue;
        x = last[i];
        _a = -1;
        L = 0, R = c[color[i]].size()-1;
        while(L <= R){
            Mid = (L + R) >> 1;
            if(c[color[i]][Mid] <= x){
                _a = Mid;
                L = Mid + 1;
            }else{
                R = Mid - 1;
            }
        }
        if(c[color[i]][_a] == i) --_a;
        if(_a != -1){
            ans += _a+1;
        }
    }
    printf("%d",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/qixingzhi/p/9250021.html