bzoj
非常巧妙的一道題
類似[hnoi影魔]
每個點會給左右第乙個大於它的點對產生貢獻
可以用單調棧求出
這裡有點小細節,就是處理相等的點時,最左邊的點管左邊的貢獻,最右邊的點管最右邊的貢獻
然後對於每個點,求出了一對\(x, y\)
那麼,對於詢問區間\(l,r\)
答案就是有多少個\(x,y\)在區間\(l,r\)之間, 即\(l<=x<=r\) && \(l<=y<=r\)
再加上相鄰的點對
這就可以用二維數點做
但是有沒有更優秀的做法呢?
我們設\(a[pos]\)為區間\([l,r]\)之間最大的數
那麼\(x\)在\([l,pos-1]\)之間的點對,\(y\)一定不會越過\(pos\)
那麼只要求出\([l,pos-1]\)之間有多少\(x\),就可以求出有多少點對\((x,y)\)在\([l,pos-1]\)
同理另一半也可以求出
那麼,字首和就可以解決問題了
#include#define ll long long
#define rg register
using namespace std;
templateinline void read(t &x)
templateinline void write(t x)
if (x < 0) x = -x, putchar('-');
int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
for (rg int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}const int n = 300010;
int n, m, a[n];
int f[21][n], g[21][n];
int query(int l, int r)
int l[n], r[n];
int suml[n], sumr[n];
int q[n];
int main()
else
int top = 0;
for (int i = 1; i <= n; i++)
top = 0;
for (int i = n; i; i--)
/*for (int i = 1; i <= n; i++)
printf("%d %d\n", l[i], r[i]);*/
for (int i = 1; i <= n; i++)
suml[l[i]]++, sumr[r[i]]++;
for (int i = 1; i <= n; i++)
suml[i] += suml[i - 1], sumr[i] += sumr[i - 1];
int lastans = 0;
while (m--)
return 0;
}
BZOJ3956 Count(單調棧 線段樹)
傳送門 感覺這道題蘊含了一些十分巧妙的性質。可以發現好點對實際上很少,最多不超過2n個。當乙個點作為點對中的較小點的時候,最多只能和它左邊第乙個大於等於它的點以及它右邊第乙個大於等於它的點配對。維護乙個單調遞減的棧就可以求出所有的好點對。還可以發現對於一段詢問的區間,找出區間中最大的點的位置,那麼區...
BZOJ 3956 Count 單調棧 ST表
題目傳送門 挺有思想的一題,但如果弄清楚了思路這題還是挺簡單的。首先我們可以發揮一下自己的腦洞,發現所有的好集對不可能相交。那麼我們可以刷兩遍單調棧,求出每個點作為區間左端點或右端點的次數。對於給定區間,我們先找出區間中最大值所在的位置,顯然所有的好集對最多以這個位置為左端點或右端點,不可能包含這個...
BZOJ3956 Count 主席樹 單調棧
3 2 0 2 1 2 1 11 303 m,n 3 10 5,ai 10 9 題解 影魔那道題的簡化版,依舊用單調棧記錄每個數左 右 邊第乙個比它大的數為ld i rd i 那麼合法的點對只有 i,i 1 和 ld i rd i 兩種。為了防止重複,我們記錄每個數左邊第乙個 它的數le i 當且僅...