[FROM WOJ]#1826 选择客栈

#1826 选择客栈

为什么我会写这道题题解?令人窒息。
其实这道题也可以用数据结构。

题面
丽江河边有n 家很有特色的客栈,客栈按照其位置顺序从 1 到n 编号。每家客栈都按照某一种色调进行装饰(总共 k 种,用整数 0 ~ k-1 表示),且每家客栈都设有一家咖啡店,每家咖啡店均有各自的最低消费。

两位游客一起去丽江旅游,他们喜欢相同的色调,又想尝试两个不同的客栈,因此决定分别住在色调相同的两家客栈中。晚上,他们打算选择一家咖啡店喝咖啡,要求咖啡店位于两人住的两家客栈之间(包括他们住的客栈),且咖啡店的最低消费不超过 p 。

他们想知道总共有多少种选择住宿的方案,保证晚上可以找到一家最低消费不超过 p元的咖啡店小聚。

输入
第一行三个整数n ,k ,p,每两个整数之间用一个空格隔开,分别表示客栈的个数,色调的数目和能接受的最低消费的最高值;

接下来的n 行,第 i+1 行两个整数,之间用一个空格隔开,分别表示 i 号客栈的装饰色调和i 号客栈的咖啡店的最低消费

输出
输出只有一行,一个整数,表示可选的住宿方案的总数。

样例输入
5 2 3
0 5
1 3
0 2
1 4
1 5

样例输出
3

SOL
这种题可以用各种方法做,我的这种方法比较慢……蒟蒻就是这样(唉我Tcl)。
f [ i ] f[i] 表示前i个客栈的cafe有几个小于等于 p p (有点像DP),vector数组 c [ i ] c[i] 用来存 i i 色调的每个旅馆的各个编号,然后乱搞
对于每种色调的旅馆,都有一个数量 t o t tot (即vector数组的size),对于第 i i 种色调,如果色调为i的旅馆两两组合都合法, a n s ans 就加上 t o t ( t o t 1 ) / 2 tot*(tot-1)/2 ,接下来只要能处理出不合法的情况就可以了,于是用到之前预处理好的 f [ ] f[] 数组。
对于 c [ i ] c[i] 当中相邻的俩旅馆 c [ i ] [ j 1 ] , c [ i ] [ j ] c[i][j-1],c[i][j] ——如果她们之间没有小于等于 p p cafe,那么她们的组合便不合法,如果这样递推下去有连续 x x 组都不合法,我们统计的答案中就减去 x ( x + 1 ) / 2 x*(x+1)/2
看到这里您可能比较懵13。我画个图给您解释一下。
在这里插入图片描述
(1表示这种组合合法,0表示不合法)
如果把相邻的点连一条0或者1的边,那么就要找出所有连续的一段0边,并将其方案数减去

代码:

#include<bits/stdc++.h>
#define N 200200
using namespace std;
#define int long long
int a[N],n,k,p;
int f[N],ans;
vector<int>c[55];
signed main(){
	scanf("%lld%lld%lld",&n,&k,&p);
	for(int register i=1;i<=n;i++){
		int col;
		scanf("%lld%lld",&col,&a[i]);
		if(p>=a[i])f[i]=f[i-1]+1;
		else f[i]=f[i-1];
		c[col].push_back(i);
	}
	for(int register i=0;i<k;i++){
		int tot=c[i].size();
		if(tot<=1)continue;
		ans+=tot*(tot-1)>>1;
		int num=0;
		for(int register j=1;j<tot;j++){
			int len=f[c[i][j]]-f[c[i][j-1]-1];
			if(!len)num+=1ll;
			if(len){ans-=(num)*(num+1)>>1;num=0;}
		}
		ans-=(num)*(num+1)>>1;//特殊处理最后一次
	}
	printf("%lld",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/hzq_oi/article/details/87477001