[Noip2011] 选择客栈
Description
丽江河边有 \(n\) 家很有特色的客栈,客栈按照其位置顺序从 \(1\) 到 \(n\) 编号。每家客栈都按照某一种色调进行装饰(总共 \(k\) 种,用整数 \(0\)~\(k-1\) 表示),且每家客栈都设有一家咖啡店,每家咖啡店均有各自的最低消费。
两位游客一起去丽江旅游,他们喜欢相同的色调,又想尝试两个不同的客栈,因此决定分别住在色调相同的两家客栈中。晚上,他们打算选择一家咖啡店喝咖啡,要求咖啡店位于两人住的两家客栈之间(包括他们住的客栈),且咖啡店的最低消费不超过 \(p\) 。
他们想知道总共有多少种选择住宿的方案,保证晚上可以找到一家最低消费不超过 \(p\) 元的咖啡店小聚。
Input
第一行三个整数 \(n ,k ,p\),每两个整数之间用一个空格隔开,分别表示客栈的个数,色调的数目和能接受的最低消费的最高值;
接下来的 \(n\) 行,第 \(i+1\) 行两个整数,之间用一个空格隔开,分别表示 \(i\) 号客栈的装饰色调和 \(i\) 号客栈的咖啡店的最低消费。
Output
输出只有一行,一个整数,表示可选的住宿方案的总数。
Hint
对于 \(30\%\) 的数据,有 \(n\leq 100\);
对于 \(50\%\) 的数据,有 \(n\leq 1,000\);
对于 \(100\%\) 的数据,有 \(2 \leq n\leq 200,000\),\(0<k \leq 50\),\(0\leq p\leq 100\) , $0\leq $最低消费 \(\leq 100\)。
Solution
我竟然做了一个小时我好菜啊
构思部分
观察到如果区间 \([l,r]\) 可行的话,那么一定存在 $i\in [l,r] $ 使得 \(val[i]\leq p\)。
也就是说,对于这个区间的右端点 \(r\),如果知道了它左边有一个 \(i\) 满足 \(val[i]\leq p\),那么这个 \(i\) 左边的点都可以对答案有贡献。
稍微贪心的想,我们想找的这个 \(i\) 一定是离 \(r\) 最近的点,否则,一定可以找到离 \(r\) 更近且满足要求的点,使得答案不会变差。
那么问题就转化为了对于每一个点 \(p\),找出它左边第一个满足 \(val[i]\leq p\) 的点 \(i\),然后所有与 \(p\) 颜色相同并且在 \(i\) 左边的点都会与 \(p\) 对答案产生 \(1\) 的贡献。
实现部分
所以,我们只需要对每个颜色开一个数组 \(col[i][j]=k\) 表示所有颜色为 \(i\) 的点中从左向右第 \(j\) 个是点 \(k\)。
另外,每个点记录它左边且最接近它的一个点 \(i\) 使得 \(val[i]\leq p\) 记为 \(last\) 数组(注意这里 \(last[i]\) 是可以等于 \(i\) 的)。
然后枚举每个点 \(i\),首先在 \(col[idx[i]]\) 中二分出相同颜色这个点前面有 \(b\) 个点,同时根据 \(last[i]\) 二分出相同颜色在 \(last[i]\) 前面有 \(c\) 个点。
注意,这时候要分情况讨论了:
- 如果 \(i=last[i]\) ,也就是说无论左端点在哪,这两个人都可以去右端点吃饭,所以直接 \(ans+=b\) 即可。
- \(i!=last[i]\) 但是 \(idx[i]=idx[last[i]]\),也就是说,这个点前面满足要求的最近的点跟它颜色一样。注意到我们的 \(c\) 是 \(last[i]\) 前面 有 \(c\) 个点,如果颜色相同的话,应该算上 \(last[i]\) ,所以 \(ans+=c+1\)。
- 最后一种情况,即 \(i!=last[i]\;and\;idx[i]!=idx[last[i]]\) ,这种情况是最简单的, \(ans+=c\) 就OK了。
时间复杂度 \(\mathcal{O(nlogn)}\)。
Code
#include<cstdio>
#include<algorithm>
#define N 200005
#define ll long long
#define min(A,B) ((A)<(B)?(A):(B))
ll ans;
int n,k,p;
int qzh[N];
int val[N];
int idx[N];
int last[N];
int col[55][N];
signed main(){
scanf("%d%d%d",&n,&k,&p);
qzh[0]=p+1;
for(int i=1;i<=n;i++){
int a;
scanf("%d%d",&a,&val[i]);
idx[i]=a;
col[a][++col[a][0]]=i;
qzh[i]=min(qzh[i-1],val[i]);
last[i]=(val[i]<=p?i:last[i-1]);
//printf("i=%d,qzh=%d,last=%d\n",i,qzh[i],last[i]);
}
for(int i=1;i<=n;i++){
if(qzh[i]>p) continue;
int b=std::lower_bound(col[idx[i]]+1,col[idx[i]]+1+col[idx[i]][0],i)-col[idx[i]];
int c=std::lower_bound(col[idx[i]]+1,col[idx[i]]+1+col[idx[i]][0],last[i])-col[idx[i]]-1;
if(last[i]==i) ans+=(ll)b-1;
else if(idx[last[i]]!=idx[i]) ans+=(ll)c;
else ans+=(ll)c+1;
//printf("i=%d,b=%d,c=%d,ans=%lld\n",i,b,c,ans);
}
printf("%lld\n",ans);
return 0;
}