[NOIP提高组2011day1t2]选择客栈

时间:2020-12-31 11:29:04

我看到有人用线段树来写而且想法和我的差不多,但是代码有一点复杂,所以我就贴一下我的做法。


思路

首先一定知道纯暴力50分差不多了,所以看到k非常的小,那么就从k入手。
不知道有没有人和我一样是先枚举颜色的,那么我们来思考一下,对于每个相同颜色的客站之间[l,r],如果当前区间不能找到一个合理的咖啡厅,那么一定会影响到和他连在一起的下一个不能找到咖啡厅的区间。那么我们就将这个标记放在r这个节点上,那么第一个节点我们就当做是0。
说的形象一点,就是看这个节点能和前面多少个相同颜色的客栈组成合法的方案。
如果当前的区间是合法的,那么前面的所有点都是可以和这个点组成方案。


算法实现

每一次算出这个客栈之前有多少个相同颜色的客栈,然后我们通过一个标记,表示在与这个区间相连接的前面几个区间内有多少个不合法的区间,每一次算的时候我们就需要把这些点数减掉。
如果当前的区间是不合法的,那么我们就需要将这个标记+1,否则就将这个标记设置成0。


代码

# include <bits/stdc++.h>
# define N 200005
# define Inf 0x3f3f3f3f
using namespace std ;
struct node {
    int c , v ;
}a[N] ;
int n , k , p , ans = 0 ;
int main () {
    scanf( "%d%d%d" , &n , &k , &p ) ;
    for ( int i = 1 ; i <= n ; i ++ ) scanf( "%d%d" , &a[i].c , &a[i].v ) ;
    for ( int i = 0 ; i < k ; i ++ ) {
        int cnt = 0 , sub = Inf , tmp = 0 , re = 0 ; //re表示有多少连接的前区间不合法
        for ( int j = 1 ; j <= n ; j ++ ) {
            sub = min ( a[j].v , sub ) ;
            if ( a[j].c == i ) {
                tmp += cnt ++ ;
                if ( sub > p ) tmp = max ( tmp - ++ re , 0 ) ;
                else re = 0 ;
                sub = a[j].v ;
            }
        }
        ans += tmp ;
    }
    printf( "%d\n" , ans ) ;
    return 0 ;
}