专题突破二之优先队列、st表——,Running Median,Sequence,Buy Low Sell High,数据备份,超级钢琴,ZQC的手办

Running Median

source

对顶栈

用大根堆和小根堆一起维护

  • 若元素小于等于大根堆栈顶,放入大根堆
  • 否则放入小根堆

大根堆维护的就是前一半较小的树,小根堆维护的就是后一半较大的树

最后调整两个堆的大小(大根堆比小根堆多一)

答案就是大根堆的栈顶

#include <cstdio>
#include <queue>
using namespace std;
#define maxn 10000
priority_queue < int > MS;
priority_queue < int, vector < int >, greater < int > > IS;
int x[maxn];

int main() {
	int T, Case, n;
	scanf( "%d", &T );
	while( T -- ) {
		while( ! MS.empty() ) MS.pop();
		while( ! IS.empty() ) IS.pop();
		scanf( "%d %d", &Case, &n );
		for( int i = 1;i <= n;i ++ )
			scanf( "%d", &x[i] );
		int cnt = 0;
		printf( "%d %d\n", Case, ( n + 1 ) >> 1 );
		for( int i = 1;i <= n;i ++ ) {
			if( MS.empty() || x[i] < MS.top() ) MS.push( x[i] );
			else IS.push( x[i] );
			if( i & 1 ) {
				while( IS.size() < ( i >> 1 ) )
					IS.push( MS.top() ), MS.pop();
				while( MS.size() < ( ( i + 1 ) >> 1 ) )
					MS.push( IS.top() ), IS.pop();
				cnt ++, printf( "%d ", MS.top() );
				if( cnt == 10 ) printf( "\n"), cnt = 0;
			}
		}
		printf( "\n" );
	}
	return 0;
} 

Sequence

source

先把每行的 m m m个元素排序

一层一层的往下叠加,仅保留前 m m m小即可

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 2005
priority_queue < int > q;
int T, n, m;
int now[maxn], nxt[maxn];

int main() {
	scanf( "%d", &T );
	while( T -- ) {
		scanf( "%d %d", &n, &m );
		for( int i = 1;i <= m;i ++ )
			scanf( "%d", &now[i] );
		sort( now + 1, now + m + 1 );
		for( int k = 1;k < n;k ++ ) {
			for( int i = 1;i <= m;i ++ )
				scanf( "%d", &nxt[i] );
			sort( nxt + 1, nxt + m + 1 );
			for( int i = 1;i <= m;i ++ )
				for( int j = 1;j <= m;j ++ ) {
					int val = now[i] + nxt[j];
					if( q.size() == m && ! q.empty() && val >= q.top() ) break;
					else {
						q.push( val );
						while( q.size() > m ) q.pop();
					}
				}
			for( int i = 1;i <= m;i ++ )
				now[m - i + 1] = q.top(), q.pop();
		}
		for( int i = 1;i <= m;i ++ )
			printf( "%d ", now[i] );
		printf( "\n" );
	}
	return 0;
}

Buy Low Sell High

source

反悔贪心

如果第 i i i天的收益更好,那么弹出栈顶,选择第 i i i

e.g.

k → j → i k\rightarrow j\rightarrow i kji,最优选择为 k → i k\rightarrow i ki

在当前最优解的 j j j时,选择 k → j k\rightarrow j kj先记录临时答案,然后把 j j j的股票价放进栈

i i i的时候进行 p i − p j p_i-p_j pipj,刚好抵消 p j − p k + p i − p j = p i − p k p_j-p_k+p_i-p_j=p_i-p_k pjpk+pipj=pipk

#include <cstdio>
#include <queue>
using namespace std;
#define int long long
#define maxn 300005
priority_queue < int, vector < int >, greater < int > > q;
int n, ans;
int p[maxn];

signed main() {
	scanf( "%lld", &n );
	for( int i = 1;i <= n;i ++ )
		scanf( "%lld", &p[i] );
	for( int i = 1;i <= n;i ++ ) {
		if( ! q.empty() && q.top() < p[i] )
			ans += p[i] - q.top(), q.pop(), q.push( p[i] );
		q.push( p[i] );
	}
	printf( "%lld\n", ans );
	return 0;
}

[APIO/CTSC 2007] 数据备份

source

反悔贪心

选了 i i i就不能选 i − 1 , i + 1 i-1,i+1 i1,i+1

弥补的代价为 d i − 1 + d i + 1 − d i d_{i-1}+d_{i+1}-d_i di1+di+1di

双向链表维护

#include <queue>
#include <cstdio>
#include <iostream>
using namespace std;
#define maxn 100005
#define int long long
struct node {
	int id, l, r, val;
	node(){}
	node( int ID, int L, int R, int Val ) {
		id = ID, l = L, r = R, val = Val;
	}
}it[maxn];
struct Node {
	int id, val;
	Node(){}
	Node( int ID, int Val ) {
		id = ID, val = Val;
	}
	bool operator < ( Node t ) const {
		return val > t.val;
	}
};
priority_queue < Node > q;
int n, k, ans;
bool vis[maxn];

void Delete( int x ) {
	it[x].l = it[it[x].l].l;
	it[x].r = it[it[x].r].r;
	it[it[x].l].r = x;
	it[it[x].r].l = x;
}

signed main() {
	int last;
	scanf( "%lld %lld %lld", &n, &k, &last );
	for( int i = 1, d;i < n;i ++ ) {
		scanf( "%lld", &d );
		it[i] = node( i, i - 1, i + 1, d - last );
		q.push( Node( i, d - last ) );
		last = d;
	}
	it[0].val = it[n].val = 1e15;
	for( int i = 1;i <= k;i ++ ) {
		while( vis[q.top().id] ) q.pop(); 
		int val = q.top().val, x = q.top().id;
		q.pop(), ans += val;
		vis[it[x].l] = vis[it[x].r] = 1;
		it[x].val = it[it[x].l].val + it[it[x].r].val - it[x].val;
		q.push( Node( x, it[x].val ) );
		Delete( x );
	}
	printf( "%lld\n", ans );
	return 0;
} 

[NOI2010] 超级钢琴

source

对于每个 i i i,将其符合要求的区间扔进队列,确定最大值的位置 p o s pos pos

可以使用 s t st st表预处理最大值位置

然后大根堆,开始选择

一段区间最大值知晓了,次大值一定是在最大值的左边或者右边(刨除最大值位置)

所以直接将区间划成两部分扔进大根堆,要保证区间存在

#include <iostream>
#include <cstdio>
#include <queue>
#include <cmath>
using namespace std;
#define maxn 500005
int n, k, L, R;
int sum[maxn];
int st[maxn][20];

void init() {
	for( int i = 1;i <= n;i ++ ) st[i][0] = i;
	for( int j = 1;j < 20;j ++ )
		for( int i = 1;i + ( 1 << j ) - 1 <= n;i ++ )
			if( sum[st[i + ( 1 << j - 1 )][j - 1]] > sum[st[i][j - 1]] )
				st[i][j] = st[i + ( 1 << j - 1 )][j - 1];
			else
				st[i][j] = st[i][j - 1];
}

int query( int l, int r ) {
	int i = log2( r - l + 1 );
	if( sum[st[l][i]] >= sum[st[r - ( 1 << i ) + 1][i]] )
		return st[l][i];
	else
		return st[r - ( 1 << i ) + 1][i];
}

struct node {
	int s, l, r, t;
	node( int S, int L, int R ) { s = S, l = L, r = R, t = query( l, r ); }
	bool operator < ( node MS ) const { return sum[t] - sum[s - 1] < sum[MS.t] - sum[MS.s - 1]; }
};
priority_queue < node > q;

int main() {
	scanf( "%d %d %d %d", &n, &k, &L, &R );
	for( int i = 1;i <= n;i ++ ) {
		scanf( "%d", &sum[i] );
		sum[i] += sum[i - 1];
	}
	init();
	for( int i = 1;i <= n - L + 1;i ++ )
		q.push( node( i, i + L - 1, min( i + R - 1, n ) ) );
	long long ans = 0;
	while( k -- ) {
		node now = q.top(); q.pop();
		ans += sum[now.t] - sum[now.s - 1];
		if( now.t != now.l ) q.push( node( now.s, now.l, now.t - 1 ) );
		if( now.t != now.r ) q.push( node( now.s, now.t + 1, now.r ) );
	}
	printf( "%lld\n", ans );
	return 0;
}

「LibreOJ β Round」ZQC 的手办

source

超级钢琴的父版

操作 1 1 1直接线段树标记维护

操作 2 2 2因为保证 x x x总和不会很大,那么类似超级钢琴一样将区间切成两部分那种

用线段树查区间最小值以及最小值位置

#include <queue>
#include <cstdio>
#include <iostream>
using namespace std;
#define maxn 500005
#define inf 0x7f7f7f7f
#define lson num << 1
#define rson num << 1 | 1
#define Pair pair < int, int >
void read(int &x) {
    x = 0;
    char s = getchar();

    while (s < '0' || s > '9')
        s = getchar();

    while ('0' <= s && s <= '9') {
        x = (x << 1) + (x << 3) + (s ^ 48);
        s = getchar();
    }
}
struct node {
    int l, r;
    Pair ret;
    node() {}
    node(int L, int R, Pair p) {
        l = L, r = R, ret = p;
    }
    bool operator < (const node &t) const {
        return ret.first > t.ret.first;
    }
};
priority_queue < node > q;
int a[maxn], ans[maxn];
int t[maxn << 2], pos[maxn << 2], tag[maxn << 2];

void pushup(int num) {
    if (t[lson] <= t[rson])
        t[num] = t[lson], pos[num] = pos[lson];
    else
        t[num] = t[rson], pos[num] = pos[rson];
}

void build(int num, int l, int r) {
    if (l == r) {
        t[num] = a[l];
        pos[num] = l;
        return;
    }

    int mid = (l + r) >> 1;
    build(lson, l, mid);
    build(rson, mid + 1, r);
    pushup(num);
}

void pushdown(int num) {
    t[lson] = max(t[lson], tag[num]);
    t[rson] = max(t[rson], tag[num]);
    tag[lson] = max(tag[lson], tag[num]);
    tag[rson] = max(tag[rson], tag[num]);
    tag[num] = 0;
}

void modify(int num, int l, int r, int L, int R, int k) {
    if (t[num] >= k)
        return;

    if (R < l || r < L)
        return;

    if (L <= l && r <= R) {
        t[num] = max(t[num], k);
        tag[num] = max(tag[num], k);
        return;
    }

    pushdown(num);
    int mid = (l + r) >> 1;
    modify(lson, l, mid, L, R, k);
    modify(rson, mid + 1, r, L, R, k);
    pushup(num);
}

Pair query(int num, int l, int r, int L, int R) {
    if (r < L || R < l)
        return make_pair(inf, inf);

    if (L <= l && r <= R)
        return make_pair(t[num], pos[num]);

    pushdown(num);
    int mid = (l + r) >> 1;
    Pair ans1 = query(lson, l, mid, L, R);
    Pair ans2 = query(rson, mid + 1, r, L, R);

    if (ans1.first <= ans2.first)
        return ans1;
    else
        return ans2;
}

int main() {
    int n, m;
    read(n);

    for (int i = 1; i <= n; i ++)
        read(a[i]);

    build(1, 1, n);
    read(m);

    for (int i = 1, opt, a, b, k, x; i <= m; i ++) {
        read(opt), read(a), read(b), read(k);

        if (opt & 1)
            modify(1, 1, n, a, b, k);
        else {
            read(x);

            while (! q.empty())
                q.pop();

            int cnt = 0;
            q.push(node(a, b, query(1, 1, n, a, b)));

            while (! q.empty() && cnt < x) {
                node now = q.top();
                q.pop();
                int val = now.ret.first, pos = now.ret.second;

                if (val >= k)
                    break;
                else
                    ans[++ cnt] = val;

                if (pos != now.l)
                    q.push(node(now.l, pos - 1, query(1, 1, n, now.l, pos - 1)));

                if (pos != now.r)
                    q.push(node(pos + 1, now.r, query(1, 1, n, pos + 1, now.r)));
            }

            if (cnt < x)
                printf("-1\n");
            else {
                for (int i = 1; i <= x; i ++)
                    printf("%d ", ans[i]);

                printf("\n");
            }
        }
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值