1. 程式人生 > 實用技巧 >loj#6062. 「2017 山東一輪集訓 Day2」Pair

loj#6062. 「2017 山東一輪集訓 Day2」Pair

前言(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陣列從小到大排序:
3
5
接著進行連邊:
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);
}