BZOJ4345 [POI2016]Korale

来源:互联网 发布:wamp改mysql密码 编辑:程序博客网 时间:2024/06/16 22:16

在病房里日题真是一种独特的体验……

首先考虑求第一问,我们先把所有元素排序,我们用优先队列维护选数的集合,对每个集合维护集合里的元素的和v和最后一个元素(即最大的元素)lst,初始的时候我们把只包含最小元素的集合推入队列,那么我们取出一个队头元素之后,如果队头的lst不是最大的元素,我们只需要再向队列里推入当前集合插入lst+1后的集合以及先删除lst再插入lst+1的集合。如果lst是最大元素,那么什么都不需要做

然后考虑求第二问,记录一下前k小里有几个和为答案的,设有x个,然后我们爆搜字典序第x小的和为答案的集合,我们用线段树维护区间最小值,然后每次在线段树上查编号最小的小于等于剩余的和的数,选这个数,搜下去

证明还是比较显然的,虽然我写了这么长

第一问的正确性证明:

首先这种方法能不重不漏第构造出所有子集

用归纳法,假设能不重复地构造出前i-1个数的所有子集,那么这时我们就相当于得到了前i个数的所有保有第i个数不选的子集,因为第i个元素没选

那么如果我们还拥有前i-1个数的保证第i-1个数选的所有子集,对于每个子集我们得到一个插入第i个元素后的集合和删除第i-1个元素再插入第i个元素的集合,我们就得到了所有前i个数的保证第i个元素选的子集

这样保证第i个元素选和保证第i个元素不选的都不重不漏地获得了,我们就相当于不重不漏地得到了前i个数的所有子集

归纳的基础在i=1时显然

然后我们还要证明这样对于任意一个集合,恰巧比他大的那个集合一定能由一个小于等于他的集合由这种方法构造出来

假设存在这样一个集合,那么我们删除集合里最大的元素,如果恰巧小于这个元素的元素不在集合里,我们就将其插入集合

那么如果新的集合小于等于当前集合,我们就可以由新的集合构造出这个恰巧比当前集合大的集合

若否,则那个集合不是恰巧比当前集合大的集合

第二问时间复杂度证明:

我们会搜出一棵树,这棵树恰巧包含k个点,每个点到根的路径代表了一个和小于等于答案集合

每走一条边和每次回溯我们需要在线段树上跑一下,花费log 的时间

所以总复杂的是k log n的

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<ctime>#include<cmath>#include<algorithm>#include<iomanip>#include<bitset>#include<set>#include<map>#include<vector>#include<stack>#include<queue>using namespace std;#define MAXN 1000010#define MAXM 1010#define INF 1000000000#define MOD 1000000007#define eps 1e-8#define ll long longstruct data{ll v;int lst;data(){}data(ll _v,int _lst){v=_v;lst=_lst;}friend bool operator <(data x,data y){return x.v>y.v;}};int n,k;ll ans;priority_queue<data>q;int a[MAXN],b[MAXN];int mn[MAXN<<2];int p[MAXN],m;ll av[MAXN];bool cmp(int x,int y){return a[x]!=a[y]?a[x]<a[y]:x<y;}void build(int x,int y,int z){if(y==z){mn[x]=y;return ;}int mid=y+z>>1;build(x<<1,y,mid);build(x<<1|1,mid+1,z);mn[x]=min(mn[x<<1],mn[x<<1|1],cmp);}int find(int x,int y,int z,int l,int r,ll fv){int mid=y+z>>1;if(y==z){return a[mn[x]]<=fv?y:0;}if(y==l&&z==r){if(a[mn[x]]>fv){return 0;}if(a[mn[x<<1]]<=fv){return find(x<<1,y,mid,l,mid,fv);}else{return find(x<<1|1,mid+1,z,mid+1,r,fv);}}if(r<=mid){return find(x<<1,y,mid,l,r,fv);}else if(l>mid){return find(x<<1|1,mid+1,z,l,r,fv);}else{int re=find(x<<1,y,mid,l,mid,fv);return re?re:find(x<<1|1,mid+1,z,mid+1,r,fv);}}void dfs(int x,ll rem){int i;if(!k){return ;}if(!rem){if(!(--k)){for(i=1;i<=m;i++){printf("%d ",p[i]);}printf("\n");}return ;}for(;x<=n;x++){x=find(1,1,n,x,n,rem);if(!x){return ;}p[++m]=x;dfs(x+1,rem-a[x]);m--;}}int main(){int i;scanf("%d%d",&n,&k);k--;if(!k){printf("0\n");return 0;}for(i=1;i<=n;i++){scanf("%d",&a[i]);b[i]=a[i];}sort(b+1,b+n+1);q.push(data(b[1],1));for(i=1;i<=k;i++){data x=q.top();av[i]=x.v;q.pop();if(x.lst!=n){q.push(data(x.v+b[x.lst+1],x.lst+1));q.push(data(x.v-b[x.lst]+b[x.lst+1],x.lst+1));}}ll ans=av[k];printf("%lld\n",ans);for(i=k;av[i]==av[k];i--){}k-=i;build(1,1,n);dfs(1,ans);return 0;}/*3 7 4 3*/


0 0
原创粉丝点击