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;
}