1. 程式人生 > >BZOJ4345 POI2016Korale(構造+堆+線段樹)

BZOJ4345 POI2016Korale(構造+堆+線段樹)

通過 clas reverse line col endif names 字典 push

  註意到k與n同階,考慮構造一種枚舉子集的方式,使得盡量先枚舉較小的子集。首先sort一下,用堆維護待選子集。每次取出最小子集,並加入:1.將子集中最大數ai替換為ai+1 2.直接向子集中添加ai+1 這兩個子集(若不存在ai+1則不操作)。如此操作k次即可得到第一問的答案。

  對於正確性,我們證明當刪除一個子集後恰好比他大的下一個子集一定在堆中。采取歸納和反證。顯然每個子集都可以由上面的構造方式變換得來。歸納基礎顯然。假設該子集和比它小的所有子集已被枚舉,如果恰好比它大的這個子集不在堆裏,則說明可以通過變換得到這個子集的子集均未被枚舉,這些子集一定不大於當前子集,這與所有比它小的子集都已枚舉矛盾。

  下面構造方案。只需要算出需要找該總和下第幾小的方案,按字典序暴力dfs就可以了,dfs時保證總和不超過第一問的答案即可保證復雜度,找編號最小的可被加入的物品可以用線段樹。開始懵逼了半天線段樹在這有什麽用,然後突然醒悟字典序是讀入的而不是排序之後的……沒救了。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
using
namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<0||c>9) {if (c==-) f=-1;c=getchar();} while (c>=0&&c<=9) x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 1000010 #define ll long long int n,m,id[N],b[N],cnt,tot; int L[N<<2
],R[N<<2],tree[N<<2]; ll ans; struct data { ll x;int i; bool operator <(const data&a) const { return x>a.x; } }a[N]; priority_queue<data> q; bool cmp(const data&a,const data&b) { return a.i<b.i; } void build(int k,int l,int r) { L[k]=l,R[k]=r; if (l==r) {tree[k]=a[l].x;return;} int mid=l+r>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); tree[k]=min(tree[k<<1],tree[k<<1|1]); } int qmin(int k,int l,int r) { if (L[k]==l&&R[k]==r) return tree[k]; int mid=L[k]+R[k]>>1; if (r<=mid) return qmin(k<<1,l,r); else if (l>mid) return qmin(k<<1|1,l,r); else return min(qmin(k<<1,l,mid),qmin(k<<1|1,mid+1,r)); } int query(int k,int p,ll x) { if (L[k]==R[k]) return L[k]; int mid=L[k]+R[k]>>1; if (p>mid) return query(k<<1|1,p,x); else if (qmin(k<<1,p,mid)<=x) return query(k<<1,p,x); else return query(k<<1|1,mid+1,x); } void dfs(int k,ll s) { if (tot==0) return; if (s==ans) {tot--;if (tot==0) for (int i=1;i<=cnt;i++) printf("%d ",id[i]);return;} int p=query(1,k+1,ans-s); while (p<=n) { id[++cnt]=p; dfs(p,s+b[p]);if (tot==0) return; cnt--; p=query(1,p+1,ans-s); } } int main() { #ifndef ONLINE_JUDGE freopen("bzoj4345.in","r",stdin); freopen("bzoj4345.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(),m=read(); for (int i=1;i<=n;i++) b[i]=a[i].x=read(),a[i].i=i; a[n+1].x=0,a[n+1].i=n+1;build(1,1,n+1); sort(a+1,a+n+1);reverse(a+1,a+n+1); q.push((data){a[1].x,1}); for (int i=1;i<m;i++) { data x=q.top();q.pop(); if (x.x>ans) tot=0; ans=x.x;tot++; if (x.i<n) q.push((data){x.x-a[x.i].x+a[x.i+1].x,x.i+1}),q.push((data){x.x+a[x.i+1].x,x.i+1}); } cout<<ans<<endl; dfs(0,0); return 0; }

BZOJ4345 POI2016Korale(構造+堆+線段樹)