题意:
实际上优先队列的三个操作都是这么实现的
这个博客上说的都很清楚,不再多说
感觉这道题可以命名为并查集dp
包括题解也是,具体解释一下左偏树的使用。
左偏树,说白了就是可合并优先队列,你有两个优先队列,那么怎么才能让他合并成一个呢,stl库的优先队列是做不到的,只能手动实现左偏树,这种数据结构可以实现在o(logn)的时间内合并两个优先队列
int merge(int a, int b) {
if (!a)return b;
else if (!b)return a;
if (query[a].first > query[b].first)swap(a, b);
r[a] = merge(r[a], b);
if (dist[r[a]] > dist[l[a]])swap(l[a], r[a]);
if (!r[a]) dist[a] = 0;
else dist[a] = dist[r[a]] + 1;
return a;
}//左偏树合并操作,代表把下标为a的查询所在的左偏树和下标为b的查询所在的左偏树合并起来
实际上优先队列的三个操作都是这么实现的
1. pop 取队头操作:把要取的树的a = merge(l[a], r[a])即可
2. push 插入操作: a = merge(a, new)
3. 合并两个树操作 a = merge( u, v )
/*
hdu 5575
*/
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#include <iostream>
#define pii pair<int,int>
#define xx first
#define yy second
#define mem(a) memset( a, 0, sizeof(a) )
using namespace std;
typedef long long ll;
int l[200005];
int r[200005];
pii query[200005];//
int dist[200005];
int fa[100005];
pii ban[100005];
vector<pii>s;
int n, m;
int merge(int a, int b) {
if (!a)return b;
else if (!b)return a;
if (query[a].first > query[b].first)swap(a, b);
r[a] = merge(r[a], b);
if (dist[r[a]] > dist[l[a]])swap(l[a], r[a]);
if (!r[a]) dist[a] = 0;
else dist[a] = dist[r[a]] + 1;
return a;
}//左偏树合并操作,代表把下标为a的查询所在的左偏树和下标为b的查询所在的左偏树合并起来
struct node
{
int qe, h, out, in;// out 溢出最优解 in 未溢出最优解 qe 指向该区域内高度最低的查询
}p[100005];
int root( int x )
{
if( fa[x] == x ) return x;
else return fa[x] = root(fa[x]);
}
void getup( int id, int he )
{
int i, j, in_max = 0, out_max = 0, tmp = 0;
s.clear();
while( p[id].qe && query[p[id].qe].xx < he ){
s.push_back(query[p[id].qe]);
if( query[p[id].qe].yy == 0 ) in_max ++;
p[id].qe = merge( l[p[id].qe], r[p[id].qe] );// pop队首操作
}
sort( s.begin(), s.end() );
tmp = in_max;// 相当于从水底向水高扫一遍,水高停在哪里是最优解(此为未溢出)
out_max = in_max;
for( i = 0; i < s.size(); i ++ ){
if( i && s[i].xx != s[i-1].xx ){
out_max = max( out_max, tmp );
}
if( s[i].yy ) tmp ++;
else tmp --;
}
out_max = max( out_max, tmp );
p[id].in = max( p[id].in + in_max, p[id].out + out_max ); // 由 之前没溢出现在也没溢出 和之前溢出现在没溢出更新而来
p[id].out = p[id].out + tmp;
p[id].h = he;
}
void mrg( int p1, int p2, int he )
{
int f1, f2;
f1 = root(p1);
f2 = root(p2);
if( p[f1].h < he ) getup( f1, he );
if( p[f2].h < he ) getup( f2, he );
fa[f2] = f1;
p[f1].qe = merge( p[f1].qe, p[f2].qe );
p[f1].in += p[f2].in;
p[f1].out += p[f2].out;
}
void solve()
{
int i, j;
cin >> n >> m;
mem(l);
mem(r);
mem(dist);
mem(p);
for( i = 1; i <= n; i ++ )fa[i] = i;
for( i = 1; i <= n-1; i ++ ){
scanf("%d", &ban[i].xx);
ban[i].yy = i;
}
for( i = 1; i <= m; i ++ ){
scanf("%d %d %d", &j, &query[i].xx, &query[i].yy);
p[j].qe = merge( p[j].qe, i );
}
sort( ban+1, ban+n );
for( i = 1; i <= n-1; i ++ ){
mrg( ban[i].yy, ban[i].yy+1, ban[i].xx );
}
int ff = root(1);
getup( ff, 1e9+7 );// 最后把水上升到无线高
cout << p[ff].in << endl;
}
int main()
{
int T, cas = 1, i, j;
cin >> T;
while( T -- ){
printf("Case #%d: ", cas ++);
solve();
}
}