(n<=1000000)
【思路】
原题就是一个裸huffman树,直接贪心用堆即可。这里我们需要找到更优秀的算法。考虑手动模拟huffman树的堆的过程。对原序列排序。第一次操作比较特别,可以特判,也可以手动加0使其一般化。对于每一次操作,我们会取出序列中的前k个数,将其合并为一个数,我们另外建一个队列,每次这些新的数加到后面。有一个很优秀的性质是,新数的序列也是单调上升的。我们先从原数序列里取出前k个,由于新数的队列里可能有更小的,我们就把新数队列里最小的数,也就是第一个数和原数列中取出的k个数中最大的数比较,尝试替换,如此反复就可以保证我们取出的是最小的k个数。由于新数队列里的数不超过
个,所以我们对于每个k比较的时间均摊下来是
。时间复杂度即O(
),这是调和级数,由极限相关知识可得时间复杂度为O(
)。
NOI2015原题代码:
#include<bits/stdc++.h>
#define re register
using namespace std;
const int N=1e5+5;
inline long long red(){
long long data=0;
int w=1;char ch=0;
ch=getchar();
while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
return data*w;
}
int n,k;
long long ans=0;
typedef pair<long long,int>T;
priority_queue<T,vector<T>,greater<T> >q;
int main(){
n=red();k=red();
for(int re i=1;i<=n;i++)q.push(make_pair(red(),0));
int m=(k-1-(n-1)%(k-1))%(k-1);
for(int re i=1;i<=m;i++)q.push(make_pair(0,0));
while(q.size()>1){
long long det=0;int mx=0;
for(int re i=1;i<=k;i++)
det+=q.top().first,mx=max(mx,q.top().second),q.pop();
ans+=det;q.push(make_pair(det,mx+1));
}
cout<<ans<<"\n"<<q.top().second;
}
加强版代码:
#include<bits/stdc++.h>
const int N=2e6+5;
int n,a[N];
long long sum[N],ans,q[N];
inline long long solve(int k){
long long res=0,nw=0;int L=1,R=0,r=0;
int tmp=n%(k-1);bool flag=1;while(tmp<=1)tmp+=k-1;
while(r<=n){nw=0;
int now=r+(flag?(flag=0,tmp):k);
while(L<=R&&now>r&&(now>n||a[now]>q[L]))nw+=q[L++],--now;
res+=(nw+=sum[now]-sum[r]);q[++R]=nw;r=now;
}return res;
}
int main(){scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);std::sort(a+1,a+n+1);
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i];
for(int i=2;i<=n;i++)ans^=solve(i);
std::cout<<ans<<"\n";
}