写题思路一定要清晰后再动键盘,否则肯定死得很惨。。(被绿题虐系列)
题目要求所有满足颜色相同且两点间存在一点小于等于某值的方案数。
显然2e6的数据一般是O(n)的做法,所以从一开始就在想递推式,然后由于思路比较乱,一直没有写出来,直到今天才好好理了理思路:
对于读入到的一点,颜色为cl,价格为wi,对价格进行分类讨论:
如果价格符合要求,那么该点前的所有点与该点及该点后点的连线都可以利用这一点,所以跨越该点的连线都是合法的。所以答案应加上这一点之前所有的同色点数。
如果价格不符合要求,那么该点只能利用之前的合法点与前面的同色点连线,这样对答案的贡献就是之前所有在最后一个合法点之前的同色点数目。
发现如果每加入一个合法点时都立即更新每种颜色的合法值所需要的时间复杂度为O(k),不符合要求,所以想到可以借助懒标记思想仅仅记录最后一个合法点的位置和每种颜色最后一个不合法点的位置,在查询到某种颜色时在进行更新即若该颜色最后一个不合法点的位置小于等于最后一个合法点的位置则把这些不合法点数加入合法点数中,这样就可以避免了大量的无用计算.
code:
1 #include<iostream> 2 #include<cstdio> 3 #define rep(i,a,n) for(register int i = a;i <= n;++i) 4 using namespace std; 5 6 int read(){int x;scanf("%d",&x);return x;} 7 8 const int Maxk = 510; 9 int cn1[Maxk],cn2[Maxk],la[Maxk]; 10 int ans,cl,wi,n,p,k; 11 12 int main(){ 13 n = read(),k = read(),p = read(); 14 15 rep(i,1,n){ 16 cl = read(),wi = read(); 17 if(wi <= p){ 18 cn1[cl] += cn2[cl],cn2[cl] = 0; 19 ans += cn1[cl]; 20 cn1[cl]++; 21 la[k] = i; 22 } 23 else{ 24 if(la[cl] < la[k]){ 25 cn1[cl] += cn2[cl]; 26 cn2[cl] = 0; 27 } 28 cn2[cl]++,la[cl] = i; 29 ans += cn1[cl]; 30 } 31 } 32 33 cout << ans; 34 return 0; 35 }