QAQ很巧妙的方法 O(n)就可以做
边读入边处理
记录:
①now:为离当前客栈最近的,花费不超过p的客栈的坐标
②sum[x]:已经对答案有过贡献的(与它之后的客栈之间有最小花费不超过p的)颜色为x的客栈的数量
③tot[x]:之前出现过的颜色为x的客栈的数量
④h[x]:之前最后一次出现的颜色为x的客栈(与当前客栈颜色相同且距离当前客栈最近的客栈)的坐标
如何记录答案?
因为我们要找的是颜色相同的两个客栈之间的最小花费小于p,所以只要在两个客栈之间存在一个小于p的,那么他们就是一种方案。所以我们记录了now。如果now在上一个与它颜色相同的客栈之间,那么就对答案有贡献,这时答案应该加上前面所有与它相同颜色的客栈的数量,即tot[x],这时,sum[x]应更新为tot[x]。反之,由于我们记录的sum[x]没有被更新,还是之前的,这时的ans+=sum[x]表示的是该客栈与所有在now之前的与它颜色相同的客栈组成的方案,这时,sum[x]不用更新,因为上一个客栈并没有与当前的客栈形成一种方案。tot[x]每次都要++,同时不要忘了更新h[x]。
注意:由于题目中说两个客栈之间的客栈包括他们两个本身,所以程序中第16、17行不要忘记加等号
由于sum和tot数组及答案的更新并不好理解,所以画个图解释一下:
如图 客栈1,2,3,4,5号
上方的数字表示花费,p=3
我们主要说一下4号和5号答案的更新
(用1表示黑色,0表示红色)
4号:
目前,tot[1]=2,sum[1]=0,h[1]=2,now=3
由于在4号和2号之间有花费小于p的客栈(即h[1]<=now),这时我们把sum[1]赋值为tot[1],也就是之前出现过的颜色为黑色的客栈都能与当前客栈形成方案,ans+=sum[1]
5号:
此时,tot[1]=3,sum[1]=2,h[1]=4,now=3
因为在4号和5号之间没有小于p的客栈,此时,不更新sum[1],也就是说,sum[1]还是2,5号对答案的贡献就是5号与之前已经与后面客栈形成方案的颜色为黑的客栈,也就是1号和2号,也就是sum[1]的值
代码:
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=100+10;
int ans,now,n,k,p;
int h[maxn],tot[maxn],sum[maxn];
int main()
{
scanf("%d%d%d",&n,&k,&p);
for(int i=1;i<=n;++i)
{
int x,y;
scanf("%d%d",&x,&y);
if(y<=p) now=i;//不要忘了等号
if(h[x]<=now) sum[x]=tot[x];//等号!!
h[x]=i;
ans+=sum[x];
tot[x]++;
}
printf("%d",ans);
return 0;
}