2021 广东省赛——A. An Easy Problem(堆)

题目链接


题意:

给定两个数 n, m。 ( 1 ≤ n , m ≤ 1 0 6 ) (1≤n,m≤10 ^6 ) (1n,m106)
求第 k 大的x*y ( 1 ≤ i ≤ n , 1 ≤ j ≤ m , K ≤ n ∗ m , K ≤ 1 0 6 ) . (1≤i≤n,1≤j≤m,K\leq n*m,K≤10^6). (1in1jmKnmK106).

思路:

相当于两行数,第一行为1~n ,第二行为1~m。每次从第一行挑一个数,从第二行挑一个数,两数相乘。

1 2 3 4 
1 2 3 4 5 6 7

为了使得结果最大,我们选第一行的n,将其与第二行的所有数相乘,将乘积作为第一元素,其对应的第一行的n作为第二元素,放到优先队列中。

  • 每次将优先队列队首元素,也就是最大乘积弹出;
  • 再将其对应第一行的元素往前拨一个,乘积再放入优先队列中。

重复上述操作,得到第k大的数。

因为 k 不超过1e6,整个复杂度为O(klogn),可。

//keeping hurry and coding calm.

const int N = 200010, mod = 1e9 + 7;

ll T, n, m, k;
struct node{
    
    
	ll x,y,z;
	
	friend bool operator < (node a,node b){
    
    
		return a.x < b.x;
	}
};

int main(){
    
    
	IOS;
	
	cin>>n>>m>>k;
	
	priority_queue<node> que;
	
//	if(n>m) swap(n,m);
	
	for(int i=1;i<=m;i++)
	{
    
    
		que.push({
    
    n*i,n,i});
	}
	
	while(k--)
	{
    
    
		ll x=que.top().x,y=que.top().y,z=que.top().z;
		que.pop();
		
		if(k==0) cout<<x;
		
		if(y) y--,x-=z,que.push({
    
    x,y,z});
	}
	
	return 0;
}

思路2:二分答案

二分最终的乘积mid,找到满足 前面至少有n*m-k+1个乘积 的最左边的一个数。
check:
遍历第一行的数 i :1~n,min(m,mid/i)累加值 就是在mid前面的个数。

bool check(int mid)
{
    
    
	cnt=0;
	for(int i=n;i>=1;i--)
	{
    
    
		cnt+=min(m,mid/i);
	}
	if(cnt>=k) return 1;
	return 0;
}

signed  main(){
    
    
	cin>>n>>m>>k;
	k=n*m-k+1;
	
	int l=1,r=n*m;
	while(l<r)
	{
    
    
		int mid=l+r>>1;
		if(check(mid)) r=mid;
		else l=mid+1;
	}
	cout<<l;
	
	return 0;
}

但是这个复杂度最坏为O(n*log(nm)),理论上是过不了的,不知道是不是数据水了。

Guess you like

Origin blog.csdn.net/Mr_dimple/article/details/121302554