zoukankan      html  css  js  c++  java
  • [bzoj4345][POI2016]Korale_堆_贪心_线段树_dfs

    bzoj4345 POI2016 Korale

    题目链接https://lydsy.com/JudgeOnline/problem.php?id=4345

    数据范围:略。


    题解

    由于$k$的范围问题,我们很容易想到优先队列。

    至于从每个状态怎么往下一个转移就是这个题的精髓。

    我们先考虑第一问:

    第一问没有字典序的限制,我们把所有的数按照从小到大排序。

    堆里维护二元组$(Sum, id)$表示这种选取方式的和位$Sum$,最大下标为$id$。

    它可以转移到$(Sum - a_{id} + a_{id+1}, id+1)$和$(Sum + a_{id + 1}, id + 1)$。

    这一想是显然的,但是不咋好想...有点超级钢琴的味道。

    下面我们考虑第二问:

    第二问我们爆搜即可,想求出来当前下标(不排序)到最后一个数这个区间内,小于当前剩余和的最小下标的数是啥,然后暴力搜下去即可。

    这个过程可以用线段树维护。

    至于复杂度为什么是对的?因为我们每时每刻都保证了所有的枚举和都是小于第一问的值的,即使枚举到了第一问的值也在接受范围内。

    言外之意我们枚举的每一个值,都是前$k-1$中的一个。

    代码

    #include <bits/stdc++.h>
    
    #define ls p << 1 
    
    #define rs p << 1 | 1 
    
    #define N 1000010 
    
    using namespace std;
    
    typedef long long ll;
    
    char *p1, *p2, buf[100000];
    
    #define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++ )
    
    int rd() {
    	int x = 0;
    	char c = nc();
    	while (c < 48) {
    		c = nc();
    	}
    	while (c > 47) {
    		x = (((x << 2) + x) << 1) + (c ^ 48), c = nc();
    	}
    	return x;
    }
    
    priority_queue <pair<ll, int> > q;
    
    int a[N], b[N], Same, mn[N << 2];
    
    ll ans[N];
    
    inline void pushup(int p) {
    	mn[p] = min(mn[ls], mn[rs]);
    }
    
    void build(int l, int r, int p) {
    	if (l == r) {
    		mn[p] = b[l];
    		return;
    	}
    	int mid = (l + r) >> 1;
    	build(l, mid, ls), build(mid + 1, r, rs);
    	pushup(p);
    }
    
    int query(int x, ll y, int l, int r, int p) {
    	if (x <= l) {
    		if (mn[p] > y) {
    			return 0;
    		}
    		if (l == r) {
    			return l;
    		}
    	}
    	int mid = (l + r) >> 1;
    	if (x <= mid) {
    		int mdl = query(x, y, l, mid, ls);
    		if (mdl) {
    			return mdl;
    		}
    	}
    	return query(x, y, mid + 1, r, rs);
    }
    
    int top, st[N], n, k;
    
    void dfs(int p, ll re) {
    	if (!Same) {
    		return;
    	}
    	if (!re) {
    		Same -- ;
    		if (!Same) {
    			for (int i = 1; i <= top; i ++ ) {
    				printf("%d ", st[i]);
    			}
    			puts("");
    		}
    		return;
    	}
    	for (int i = p + 1; i <= n; i ++ ) {
    		i = query(i, re, 1, n, 1);
    		if (i) {
    			st[ ++ top] = i;
    			dfs(i, re - b[i]);
    			top -- ;
    		}
    		else {
    			break;
    		}
    	}
    }
    
    int main() {
    	n = rd(), k = rd() - 1;
    	for (int i = 1; i <= n; i ++ ) {
    		a[i] = b[i] = rd();
    	}
    	sort(a + 1, a + n + 1);
    	q.push(make_pair(-a[1], 1));
    	for (int i = 1; i <= k; i ++ ) {
    		ans[i] = -q.top().first;
    		int x = q.top().second;
    		q.pop();
    		if (x < n) {
    			q.push(make_pair(-(ans[i] - a[x] + a[x + 1]), x + 1));
    			q.push(make_pair(-(ans[i] + a[x + 1]), x + 1));
    		}
    	}
    	// for (int i = 1; i <= k; i ++ ) {
    	// 	printf("%lld ", ans[i]);
    	// }
    	// puts("");
    	cout << ans[k] << endl ;
    	for (int i = k; i; i -- ) {
    		if (ans[i] != ans[k]) {
    			break;
    		}
    		Same ++ ;
    	}
    	// cout << Same << endl ;
    	build(1, n, 1);
    	dfs(0, ans[k]);
    	return 0;
    }
    
  • 相关阅读:
    面向对象课程设计-FlappyCharacter(个人)
    学生成绩管理系统——个人博客
    学生成绩管理系统(个人)
    JAVA课程设计个人博客----FlappyBird
    Java课程设计——彩票购买抽奖系统
    彩票抽奖程序—个人博客
    彩票购买抽奖程序——个人博客
    面对对象综合设计-贪吃蛇个人博客
    面向对象综合设计-贪吃蛇
    面向对象程序综合设计—课程设计
  • 原文地址:https://www.cnblogs.com/ShuraK/p/11795638.html
Copyright © 2011-2022 走看看