hdu 5575 Discover Water Tank

A lot of frogs are living in a water tank, but none of them know exactly how much water are there.

The water tank has an infinite height, but with a narrow bottom. The length of the tank is N, and the width is only 1.

Now N−1 boards has divided the tank into N parts, each with an 1×1 bottom. Boards may have different heights. Water cannot flow through the boards, but can run freely if the water level is higher, following the basic physical rules.

The Frog King wants to know more details of the tank, so he sent someone to choose M positions and find whether there’s water at that position.

For example, each time he’ll choose(x,y), checking the xth part of the tank(1≤x≤N), counting from left to right, and find whether there’s water at height (y+0.5).

Now the King gets M
results, but he finds some of them might be wrong. The King wants to find out the maximum possible number of the correct results.
Input
First line contains an integer T, which indicates the number of test cases.

Every test case begins with two integers N and M, which is the numbers of tanks and numbers of results.

The second line of each test case contains N−1 integers, h1, h2, ⋯, hN−1, and hi indicates the height of ith board’s height.

Then M lines follow, the ith line, formated as ‘x y z’, indicates ith result. z is 0 if there is no water at height (y+0.5) in xth tank, otherwise z is 1.

⋅ 1≤T≤100.

⋅ For 90% data, 1≤N≤1000 and 1≤M≤2000

⋅ for 100% data, 1≤N≤105 and 1≤M≤2⋅105.

⋅ 1≤hi≤109 for all 1≤i≤N−1.

⋅ for every result, 1≤x≤N, 1≤y≤109 and z is either 0 or 1
.
Output
For every test case, you should output ” Case #x: y”,where x indicates the case number and counts from 1, and y
is the maximum possible number of the correct results.
Sample Input

2
3 4
3 4
1 3 1
2 1 0
2 2 0
3 3 1
2 2
2
1 2 0
1 2 1

Sample Output

Case #1: 3
Case #2: 1

Hint

In the first sample case, if the 1-st condition is true, then there's water at height 3.5 in the first part. The water will run over the board with height 3, so the water level in the second part should also be the same as the first part, which contradicts the 2-nd and 3-rd observation.

题意:有一个水罐车,长为n宽为1,无限高,现在在里面插入n-1个板子,把它分成n个底为1×1的块,有m个条件,x,y,z,表示如果第x个块里高为y+0.5的地方有水,那么z为1,没水,z为0。问这m个条件最多有多少个是同时正确的。如果水高于板子会溢出。
做法:左偏树是什么,啊,真香,左偏树可以合并两个有序集合并保证有序,对于某一个区间q,它可以看作是有这个区间中最高的点分开的两个区间q1,q2,这个区间的最大贡献:1,水不超过这个最高的点,就是这q1,q2水不溢出的情况下的最大贡献,2:水超过了这个点,枚举高这超过这个最高点但是不大于q区间的两边的板子,的最大值,合并区间的可选择点可以用左偏树。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+100;
struct node{
    int h,op;
    bool operator<(const node &p)const{
        if(h != p.h) return h < p.h;
        return op < p.op;
    }
};
node pt[N];
int l[N],r[N],head[N],pre[N],v[N],mx[N],h[N],w[N];
int ls[N],rs[N];
int que[N];
int Find(int x){return x == pre[x]?x:pre[x] = Find(pre[x]); }
int merge(int x,int y){
    if(x == 0) return y;
    if(y == 0) return x;
    if(pt[y] < pt[x]) swap(x,y);
    r[x] = merge(r[x],y);
    if(h[l[x]] < h[r[x]]) swap(l[x],r[x]);
    h[x] = h[l[x]]+1;
    return x;
}
int del(int x){
    return merge(l[x],r[x]);
}
int main(){
    int T;
    cin >> T;
    for(int kase = 1;kase <= T;kase ++){
        int n,m;
        scanf("%d %d",&n,&m);
        for(int i = 1;i <n;i ++){
            scanf("%d",&w[i]);
        }
        w[0] = 1e9+7;
        w[n] = 1e9+7;
        vector<pair<int,int> > vp;
        for(int i = 1;i < n;i ++) vp.push_back({w[i],i});
        sort(vp.begin(),vp.end());
        memset(head,0,sizeof(head));
        memset(h,0,sizeof(h));
        memset(v,0,sizeof(v));
        memset(mx,0,sizeof(mx));
        memset(l,0,sizeof(l));
        memset(r,0,sizeof(r));
        for(int i = 1;i <= n;i ++) pre[i] = rs[i] = i,ls[i] = i-1;
        for(int i = 1;i <= m;i ++){
            int x;
            scanf("%d %d %d",&x,&pt[i].h,&pt[i].op);
            if(pt[i].op == 0) v[x]++;
            head[x] = merge(head[x],i);
        }
        for(int i = 1;i <= n;i ++){
            int mn = min(w[i],w[i-1]);
            int now = v[i];
            int res = now;
            while(head[i]&&pt[head[i]].h < mn){
                if(pt[head[i]].op == 0) now --;
                else now ++;
                res = max(res,now);
                head[i] = del(head[i]);
            }
            mx[i] = res;
            v[i] = now;
        }
        for(int i = 0;i < n-1;i ++){
            int pos = vp[i].second;
            int fa = Find(pos),fb = Find(pos+1);
            int L = ls[fa],R = rs[fb];
            int mn = min(w[L],w[R]);
            pre[fb] = fa;
            ls[fa] = L,rs[fa] = R;
            head[fa] = merge(head[fa],head[fb]);
            int now = v[fa]+v[fb];
            int res = mx[fa]+mx[fb];
            while(head[fa]&&pt[head[fa]].h < mn){
                if(pt[head[fa]].op == 0) now --;
                else now ++;
                res = max(now,res);
                head[fa] = del(head[fa]);
            }
            v[fa] = now;
            mx[fa] = res;
        }
        printf("Case #%d: ",kase);
        cout << mx[1] << endl;
    }
    return 0;
}

左偏树可以解决的问题是对于两个有序的序列,如果需求是从小到大枚举,那么如果想合并两个序列,然后再有序枚举,那么可以使用左偏树。

猜你喜欢

转载自blog.csdn.net/zstu_zy/article/details/81100332