1. 程式人生 > >HDU 5575 Discover Water Tank 並查集+左偏樹

HDU 5575 Discover Water Tank 並查集+左偏樹

不妨假定初始答案為所有的無水詢問,因為這樣一定沒有衝突。

然後列舉有水詢問、水位線到這裡時,答案能否更優。

若水位線達到某一高度,則可能淹沒旁邊的水箱,那麼實際就變成了一個大水箱,所以考慮用並查集來優化,為保證合併順序正確,先對有水詢問按水位高度排序。

下面思考更新答案,樸素的做法是列舉此水位線下的有水詢問和無水詢問的差,但實際上因為有水詢問的水位高度已經排序,我們會做大量重複列舉,所以考慮排序後來做,但是因為有合併操作,所以考慮用左偏樹來維護無水詢問。

 時間複雜度O(α(n) + mlogm)

#include <bits/stdc++.h>

using namespace std;

#define N 100010
#define INF INT_MAX
int n,m,l_b[N],r_b[N],l_t[N],r_t[N],x[N],o[N];

int tot,v[N*2],l[N*2],r[N*2],d[N*2],heap[N*2];

struct node
{
    int x,y;
    bool operator<(const node&b) const
    {
        return y == b.y ? x < b.x : y < b.y;
    }
}q[N*2];
int cnt,ans;
int fa[N];

int merge(int x,int y)
{
    if(!x)
        return y;
    if(!y)
        return x;
    if(v[x] > v[y])
        swap(x,y);
    r[x] = merge(r[x],y);
    if(d[l[x]] < d[r[x]])
        swap(l[x],r[x]);
    d[x] = d[r[x]] + 1;
    return x;
}

inline int init(int x)
{
    tot++;
    v[tot] = x;
    l[tot] = r[tot] = d[tot] = 0;
    return tot;
}

inline int insert(int x,int y)
{
    return merge(x,init(y));
}

inline int top(int x)
{
    return v[x];
}

inline int pop(int x)
{
    return merge(l[x],r[x]);
}

int getfa(int x)
{
    if(x == fa[x])
        return x;
    fa[x] = getfa(fa[x]);
    return fa[x];
}

void mergeSet(int _x,int _y)
{
    _x = getfa(_x);
    _y = getfa(_y);
    if(_x == _y)
        return;
    fa[_x] = _y;
    if(_x < _y)
    {
        l_b[_y] = l_b[_x];
        r_t[l_t[_x]] = _y;
        l_t[_y] = l_t[_x];
    }
    else
    {
        r_b[_y] = r_b[_x];
        l_t[r_t[_x]] = _y;
        r_t[_y] = r_t[_x];
    }
    heap[_y] = merge(heap[_x],heap[_y]);
    x[_y] += x[_x];
    o[_y] += o[_x];
}

int main()
{
    int t;
    cin >> t;
    for(int ii = 1; ii <= t; ii++)
    {
        ans = cnt = tot = 0;
        cin >> n >> m;
        memset(heap,0,sizeof(heap));
        memset(o,0,sizeof(o));
        memset(x,0,sizeof(x));
        int _x,_y,_z,_h;
        l_b[1] = INF;
        r_b[n] = INF;
        l_t[n] = n-1;
        for(int i = 1 ; i < n; i++)
        {
            scanf("%d",&_x);
            l_b[i+1] = r_b[i] = _x;<span style="white-space:pre">				</span>//合併時需要的資訊
            l_t[i] = i - 1;
            r_t[i] = i + 1;
        }
        for(int i = 0; i < m; i++)
        {
            scanf("%d%d%d",&_x,&_y,&_z);
            if(_z == 0)
            {
                ans++;
                heap[_x] = heap[_x] ? insert(heap[_x],_y) : init(_y);
            }
            else
            {
                cnt++;
                q[cnt].x = _x;
                q[cnt].y = _y + 1;
            }
        }
        for(int i = 1; i <= n; i++)
            fa[i] = i;
        sort(q+1,q+1+cnt);
        for(int i = 1; i <= cnt; i++)
        {
            _h = q[i].y;
            _x = q[i].x;
            _x = getfa(_x);
            while(_h > l_b[_x])
            {
                mergeSet(l_t[_x],_x);
                _x = getfa(_x);
            }
            while(_h > r_b[_x])
            {
                mergeSet(r_t[_x],_x);
                _x = getfa(_x);
            }
            while(heap[_x] && top(heap[_x]) < _h) {   //x,o陣列統計無水 有水詢問的和
                heap[_x] = pop(heap[_x]);
                x[_x]++;
            }
            o[_x]++;
            if(o[_x] >= x[_x]) {
                ans += (o[_x] - x[_x]);
                o[_x] = x[_x] = 0;
            }
        }
        printf("Case #%d: %d\n",ii,ans);
    }
    return 0;
}