【NOI2015荷马史诗加强版】【Huffman树】epic

在这里插入图片描述
(n<=1000000)
【思路】
原题就是一个裸huffman树,直接贪心用堆即可。这里我们需要找到更优秀的算法。考虑手动模拟huffman树的堆的过程。对原序列排序。第一次操作比较特别,可以特判,也可以手动加0使其一般化。对于每一次操作,我们会取出序列中的前k个数,将其合并为一个数,我们另外建一个队列,每次这些新的数加到后面。有一个很优秀的性质是,新数的序列也是单调上升的。我们先从原数序列里取出前k个,由于新数的队列里可能有更小的,我们就把新数队列里最小的数,也就是第一个数和原数列中取出的k个数中最大的数比较,尝试替换,如此反复就可以保证我们取出的是最小的k个数。由于新数队列里的数不超过 O ( n / k ) O(n/k) 个,所以我们对于每个k比较的时间均摊下来是 O ( n / k ) O(n/k) 。时间复杂度即O( i = 1 n n i \sum_{i=1}^n \frac{n}{i} ),这是调和级数,由极限相关知识可得时间复杂度为O( n ln n n\ln n )。
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";
}
发布了106 篇原创文章 · 获赞 22 · 访问量 5505

猜你喜欢

转载自blog.csdn.net/weixin_44111457/article/details/101644613