归并法的另类活用

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/83539988

归并排序是OI中的一种常用方法,因为它借助了对于两个有序队列,合并成一个有序队列,只需要看队头,并以此达到O(n)的合并复杂度,至于分治只是活用它的一种方式罢了。
但是看队头的思想并不只局限于队列。
比如说丑数这个经典题:


对于一给定的素数集合 S = {p1, p2, …, pK},考虑一个正整数集合,该集合中任一元素的质因数全部属于S。这个正整数集合包括,p1、p1p2、p1p1、p1p2p3…(还有其它)。该集合被称为S集合的“丑数集合”。注意:我们认为1不是一个丑数,你的工作是对于输入的集合S去寻找“丑数集合”中的第N个“丑数”。


可能有人会问,这哪里有队列呢?
实际上是有的,不难发现,每一个丑数都可以写成,它小的丑数或者1乘上pi(1<=i<=k),那么如果我们已经求出了前i个丑数a1~ai,第i+1个丑数就一定可以表示为ar * pj(1<=r<=i && 1<=j<=k),那么我们只需要求前i个丑数乘上每一个p的积中大于第i个丑数的最小值,即是第i+1个丑数。
那么实际上我们就有了k个队列,第k个是a1 * pk , a2 * pk,…ai * pk
那么我们就可以像归并排序一样,记录第k个队列前几项被选过了。
当k很大的时候可以用堆或优先队列来维护,时间复杂度我只能说<O(nk)

Code:

#include<cstdio>
#include<algorithm>
using namespace std;

int n,k,p[105],seq[100005],cf[105];
int s(int now){
	return p[now]*seq[cf[now]];
}

int heap[105],pos[105],size;
void swap(int u,int v){
	heap[u]^=heap[v],heap[v]^=heap[u],heap[u]^=heap[v];
	pos[heap[u]]=u,pos[heap[v]]=v;
}

void shift(int now){
	while(s(heap[now>>1])>s(heap[now])) swap(now,now>>1);
}

void push(int now){
	heap[++size]=now;
	pos[now]=size;
	shift(size);
}

void modown(int now){
	int nxt;
	while(now<<1<=size){
        nxt=now<<1;
        if(s(heap[nxt])>s(heap[nxt+1]) && nxt+1<=size) nxt++;
        if(s(heap[nxt])>s(heap[now])) return;
		if(nxt>size) return;
        swap(now,nxt);
        now=nxt;
    }
}

int main(){
	int val;
	scanf("%d%d",&k,&n);
	seq[0]=1;
	for(int i=1;i<=k;i++) scanf("%d",&p[i]),push(i);
	for(int i=1;i<=n;i++)
	{
		val=s(heap[1]);
		seq[i]=val;
		while(size && s(heap[1])==val){
			cf[heap[1]]++;
			modown(1);
		}
	}
	printf("%d",seq[n]);
}

再看这样一道题:(NOI.AC #64. sort)


sort
题目描述
给定两个长度为 n 的数组 A 和 B,对于所有的 ai+bj 从小到大排序,并输出第 L个到第 R 个数。

输入格式
第一行三个数 n,L,R 。

第二行一共 n 个数,描述 A 。

第三行一共 n 个数,描述 B 。

输出格式
按顺序输出第L 个到第 R 个数。

样例
输入
2 1 4
1 3
2 4
输出
3 5 5 7
数据范围
n≤105,1≤L≤R≤n2,R−L<105,1≤ai,bi≤109n≤105,1≤L≤R≤n2,R−L<105,1≤ai,bi≤109 。


求第L个数字的大小很简单,二分就行。
求L~R的每个数字,将a,b排序
然后我们就有了n个队列,第i个队列的元素是:
ai + b1 , ai + b2 , ai + b3…ai + bn
然后就求大于第L个数字的R-L+1个数就行。

Code:

#include<bits/stdc++.h>
#define maxn 100005
#define LL long long
using namespace std;

LL l,r,n;
int a[maxn],b[maxn];
struct node
{ 
	int sum,id;
	node(int a=0,int b=0):sum(a),id(b){}
	bool operator<(const node &B)const{ return sum==B.sum ? id < B.id : sum > B.sum; }
};
priority_queue<node>q;

int main()
{
	scanf("%lld%lld%lld",&n,&l,&r);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++) scanf("%d",&b[i]);
	sort(a+1,a+1+n),sort(b+1,b+1+n);
	
	LL L = 0 , R = 2000000000 , Mid;
	for(;L<R;)
	{
		Mid = (L+R) >> 1;
		LL ret = 0;
		for(int i=1,j=n;i<=n;i++)
		{
			for(;j>=1 && a[i] + b[j] > Mid;j--);
			ret += j;
		}
		if(ret >= l) R = Mid;
		else L = Mid + 1;
	}
	
	for(int i=1,j=n;i<=n;i++)
	{
		for(;j>=1 && a[i] + b[j] >= L;j--);
		if(j<n)
			q.push(node(a[i]+b[j+1],j+1));
	}
	
	bool flag = 0;
	LL ans = r-l+1;
	for(int sum,id;ans;)
	{
		sum = q.top().sum , id = q.top().id , q.pop();
		if(flag) printf(" ");
		else flag = 1;
		printf("%d",sum);
		if(id<n)q.push(node(sum-b[id]+b[id+1],id+1));
		ans--;
	}
}

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/83539988
今日推荐