loj#6062. 「2017 山東一輪集訓 Day2」Pair
阿新 • • 發佈:2020-10-05
前言(feihua):
這個題目折磨了我大概兩個小時,我是在圖論的題單裡面看到這道題目的。一開始看著,感覺有點像資料結構,但是既然是放在圖論裡面,怎麼可能是單純的資料結構題!(啪啪打臉,還真的可以純資料結構)
想出來了一個合理的圖論摻雜資料結構的演算法,但是我太菜了,找ZCW神仙問了3次才最終寫出程式碼...
思路:
首先這種型別的題目的思路,首先將a陣列中的元素a[i]轉化為h-a[i].
然後建圖,連線所有b[j]使得b[j] >= h-a[i],表示b[j]是可以和a[i]匹配的,但是有個問題,那就是n和m大到了1.5 * 10^5,邊數可能卡到特別多,然後就炸了.......
但是不慌,我接著想到了,把a(轉化成h-a[i]後的陣列),b陣列排序.
排序後:
假如b[j-1]可以連到a[i]( 也就是b[j-1] >=a[i]),毫無疑問b[j]也可以連到a[i],(b[j] >= b[j-1] >= a[i]),這時候b[j]就直接連到b[j-1],然後再看有哪些是b[j]能連而 b[j-1]不能連的。
這樣存的邊數大概是 n + m 條
此時我們發現這樣的圖構成了一棵樹,而且對於a中的元素必定是葉子節點.
以樣例為例:
input:
5 2 10
5 3
1 8 5 5 7
output:
2
不難發現能夠成功匹配的是[2,3]以及[4,5]
按照我的處理方式來做,首先將a陣列轉化為:
9
2 5 5 3
然後將a陣列從小到大排序:
2 3 5 5 9
同時將b陣列從小到大排序:
35
接著進行連邊:
b[1] ---> a[1] (b[1] >= a[1])
b[1] ---> a[2] (b[1] >= a[2])
b[2] ---> b[1] (b[2] >= b[1](此刻發現b[1]已經無法與a[3]匹配了)
b[2] ---> a[3] (b[2] >= a[3])
b[2] ---> a[4] (b[2] >= a[4])
b[2] ---> a[5] (b[2] >= a[5])
然後就形成了一棵樹,帶括號的表示的是a陣列中的元素編號:
2
/ \ \ \
/ \ \ \1
(3)(4)(5)
/
\
(1) (2)
但是接下來要查詢的是原來a中的編號,我們需要將排序後的a[i]在原來的未排序中陣列a中對應的位置找出來:
例如現在a[1]對應的是原來沒有排序的a[2]
現在的a[2] 對應原來的a[5],我們在樹上把編號還原
2
/ \ \ \
/ \ \ \1
(3)(4)(1)
/ \
(2) (5)
接著進入了判斷環節,類似於滑動視窗,我們觀察到如果判斷了當前連續子序列,那麼下一個連續子序列就只要刪除一個點並且加入一個點
所以我們需要在logn的時間內判斷是否可行。
以樣例判斷 [3,4]為例,我們發現,假如一個非葉子節點它的深度小於 它相連的葉子節點數與它的所有祖先節點連線的葉子節點的和
(這裡的相連指的是現在還在樹上的,例如[3,4],現在只有3和4這兩個a中元素節點在樹上,這裡點"2"就有2個直接相連的葉子節點,但是它的深度為1)就無解
相當於維護一個字首和,維護到當前這個非葉子節點它的祖先與它一共有多少個葉子節點,同時減去這個非葉子節點的深度,如果整棵樹上沒有一個非葉子節點的對應的權值大於0,那麼ans++。
所以我們可以藉助線段樹求解,每次判斷連續子序列時只會有一個節點被從樹上刪去,也只有一個節點被增加,所以就相當於區間修改以及求最大值的線段樹了!
CODE:
很醜,辣眼睛,不建議觀看,按照思路來寫就行!
#include <bits/stdc++.h> using namespace std; #define int long long int n,m,h; int b[500105],f[500150]; struct node{int data,num;}a[500150]; struct tree{int l,r,Max,data,laz;}T[500150]; inline int read(); void build_tree(int l,int r,int x); void pushdown(int x); void ad(int x,int k); void add(int l,int r ,int x,int k); int get(int l,int r,int x); int cmp(node A,node B){return A.data < B.data;} signed main(){ int res = 0; n = read () , m = read () , h = read(); for (int i = 1 ; i <= m ; i ++)b[i] = read(); for (int i = 1 ; i <= n ; i ++)a[i].data = read(),a[i].num = i; for (int i = 1 ; i <= n ; i ++)a[i].data = h - a[i].data; sort ( a + 1 , a + 1 + n , cmp );sort( b + 1 , b + 1 + m ); int head = m , flag = 0;; for (int i = 1 ; i <= n ; i ++){ while (b[m-head+1] < a[i].data)head--; if (head <= 0){head++;break;} f[a[i].num] = head; } build_tree(1,m,1); for (int i = 1 ; i <= m ; i ++) add(i,i,1,-i); for (int i = 1 ; i <= n - m +1 ; i ++){ int l = i , r = i + m - 1; if (l == 1){ for (int j = l ; j <= r ; j ++){ if(f[j] != 0) add(f[j],m,1,1); else flag = j ; } if(get(1,m,1) <= 0 && flag == 0)res += 1; } else { int X = f[l-1] , Y = f[r]; if( X == 0 || Y == 0){ if (X == 0)add(Y,m,1,1),flag = X; else add(X,m,1,-1),flag = Y; } else{ if(Y > X) add(X,Y-1,1,-1); if(X > Y) add(Y,X-1,1,1); } if(get(1,m,1) <= 0 && l > flag)res += 1; } } cout << res; return 0; } int get(int l,int r,int x){ int Max = -0x3f; if( T[x].l >= l && T[x].r <= r)return T[x].Max; pushdown(x); int mid = ( T[x].l + T[x].r ) >> 1; if( l <= mid)Max = max(get(l,r,x*2),Max); if( r > mid)Max = max(get(l,r,x*2+1),Max); return Max; } inline int read(){ int x = 0 , flag = 1; char ch = getchar(); for ( ; ch > '9' || ch < '0' ; ch = getchar())if(ch == '-')flag = -1; for ( ; ch >= '0' && ch <= '9' ; ch = getchar())x = (x << 3) + (x << 1) + ch - 48 ; return x * flag; } void build_tree(int l,int r,int x){ T[x].l = l , T[x] . r = r; if (T[x].l == T[x].r){T[x].Max = 0;return ;} int mid = ( l + r ) >> 1; build_tree(l,mid,x*2);build_tree(mid+1,r,x*2+1); T[x].Max = max(T[x*2+1].Max , T[x*2].Max); } void ad(int x,int k){ T[x].Max += k; T[x].laz += k; } void pushdown(int x){ if(T[x].laz == 0)return ; ad(x*2,T[x].laz); ad(x*2+1,T[x].laz); T[x].laz = 0; } void add(int l ,int r ,int x, int k ){ if (T[x].l >= l && T[x].r <= r){ ad(x,k); return ; }pushdown(x); int mid = (T[x]. l + T[x] . r) >> 1; if (l <= mid)add(l,r,x*2,k); if (r > mid)add(l,r,x*2+1,k); T[x].Max = max(T[x*2].Max,T[x*2+1].Max); }