Wiki OI 1245 最小的N个和

本文介绍了一种解决特定问题的有效算法——K路归并结合堆(优先队列)。该算法适用于从两个长度相同的数列中各取一数相加,求得所有可能和的前n小项。通过使用堆优化数据结构,避免了直接计算和排序所有组合的高昂成本。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:http://wikioi.com/problem/1245/

算法与思路:K路归并 + 堆(优先队列),K路归并具体请参考刘汝佳《算法竞赛入门经典训练指南》P189;

题目要求从两个长度为n的数列中各取出一数相加,可得到n*n个和,输出这些和升序的前n项;

由于数据太大,不能通过先求和再排序的方式来求解,这个时候就要用到堆了;

首先将a,b两数组排序,然后将a[i] + b[1]压入堆中,设每次出堆的元素为sum = a[a] + b[b],

则将a[a] + b[b + 1]入堆,这样可以保证前n个出堆的元素为最小的n项;在实现的时候,

可以不用保存b数组的下标,通过sum - b[b] + b[b + 1]来替换a[a] + b[b + 1]来节省空间。

#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
const int N = 100005;
struct node
{
	int sum;
	int b;
	bool operator < (node a) const
	{
		return sum > a.sum;
	}
}heap[N * 2];
priority_queue<node>q;
int cmp(int x, int y)
{
	return x < y;
}
int main()
{
	int n, a[N], b[N], i;
	scanf("%d", &n);
	for(i = 1; i <= n; i++)
	    scanf("%d", &a[i]);
	for(i = 1; i <= n; i++)
	    scanf("%d", &b[i]);
	sort(a + 1, a + n + 1, cmp);
	sort(b + 1, b + n + 1, cmp);
	for(i = 1; i <= n; i++)
		q.push((node){a[i] + b[1], 1});
	for(i = 1; i <= n; i++) 
	{
		node buf = q.top();
		q.pop();
		printf("%d ", buf.sum);
		if(buf.b <= n)
		    q.push((node){buf.sum - b[buf.b] + b[buf.b + 1], buf.b + 1}); 
	}
	return 0;
}


#include<bits/stdc++.h> #define ll long long using namespace std; ll n, a, b; bool f(ll a, ll b) { string s1, s2; while (a) { s1.push_back(a % 2); a >>= 1; } while (b) { s2.push_back(b % 2); b >>= 1; } for (int i = 0; i < s1.size(); i++) if (s1[i] == 1 && s2[i] == 0) return false; return true; } int main() { ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); cin >> n; while (n--) { cin >> a >> b; if (a == b)cout << 0 << '\n'; else if (a > b) cout << 2 << '\n'; else if (f(a, b))cout << 1 << '\n'; else cout << 2 << '\n'; } return 0; } /* 位与:同1为1,否则为0 位或:有1为1,否则为0 1:1 1==1 两数相同则cnt=0 1:4 001 100 (1&0)|4=4 4:9 0100 1001 (4&0)|9=9 cout<<(3&0); 两数不同最多&0加|m则cnt=2 4:5 100 101 4|5=5 4:7 100 111 4|3=7 两数若n<m二进制位有包含关系则cnt为1 (ll)2e5*18*2=7200000转二进制暴力判断会不超时 若n>m 4:3 100 011 不存在包含关系,直接输出2 */# P9496 「RiOI-2」hacker ## 题目背景 在小树丛边坐落着一个幻想的城堡。这里是 E 国的领地,而小 E,则是 E 国之王。 现在,伟大的 E 国之王正在披挂出征。 不过听说 E 国之王遇见了两个叫 ACCEPT BOTH 的人,他们是谁? ## 题目描述 现在有正整数 $n$,有以下操作; - 「ACCEPT」。花费 $1$ 的代价,将 $n$ [二进制](https://oi-wiki.org/math/base/#%E4%BA%8C%E8%BF%9B%E5%88%B6)**按位或**上 一个正整数。 - 「BOTH」。花费 $1$ 的代价,将 $n$ [二进制](https://oi-wiki.org/math/base/#%E4%BA%8C%E8%BF%9B%E5%88%B6)**按位与**上 一个正整数。 两种操作均可使用多次(或不用),请求出将 $n$ 变为 $m$ 最小的代价。 [帮助:什么是按位与按位或](https://oi-wiki.org/math/bit/#%E4%B8%8E%E6%88%96%E5%BC%82%E6%88%96) ## 输入格式 **本题有多组数据。** 第一行一个正整数 $T$ 表示数据组数。 接下来 $T$ 行,每行两个正整数 $n, m$ 以空格隔开。 ## 输出格式 输出 $T$ 行,每行一个整数,表示答案。 ## 输入输出样例 #1 ### 输入 #1 ``` 3 1 1 4 5 1 4 ``` ### 输出 #1 ``` 0 1 2 ``` ## 说明/提示 ### 样例解释 + 对于 $n = 1$,$m = 1$,不需要任何操作。 + 对于 $n = 4$,$m = 5$,一种可行的方案为使用「ACCEPT $1$」。 + 对于 $n = 1$,$m = 4$,一种可行的方案为依次使用「ACCEPT $998{,}244{,}853$」「BOTH $14$」。 ### 数据规模与约定 **本题开启捆绑测试。** | $\text{Subtask}$ | 分值 | $T \leq$ | $n, m \leq$ | | :--------------: | :--: | :------: | :---------: | | $0$ | $30$ | $100$ | $100$ | | $1$ | $70$ | $2\times 10^5$ | $10^{18}$ | 对于所有数据,$1\le T\le2\times 10^5$,$1\le n,m\le10^{18}$。 我的代码思路是否正确
最新发布
08-11
# P6068 『MdOI R1』GCD? GCD! ## 题目描述 灵喜欢 $\mathrm{gcd}$,也就是 **最大公约数**。如果你不知道什么叫做最大公约数,你可以访问 [最大公约数 - OI Wiki](https://oi-wiki.org/math/gcd/)。 灵给了你一个正整数 $n$,要你把它分成三个 **互不相等的** 正整数 $a,b,c$ 之,使得 $\mathrm{gcd}(a,b,c)$ 最大。 ## 输入格式 **本题有多组数据**。 第一行一个正整数 $T$,表示数据组数。 接下来 $T$ 行,每行一个正整数 $n$。 ## 输出格式 对于每组数据,一行一个整数,表示答案,即最大的 $\mathrm{gcd}(a,b,c)$ 。 特别地,如果 $n$ 无法分成三个互不相等的正整数之,请输出 `-1`。 ## 输入输出样例 #1 ### 输入 #1 ``` 3 12 27 5 ``` ### 输出 #1 ``` 2 3 -1 ``` ## 说明/提示 【样例解释】 将 $12$ 分成 $2+4+6$,可以证明 $\gcd(2,4,6)=2$ 为可能达到的最大值。 将 $27$ 分成 $3+6+18$,可以证明 $\gcd(3,6,18)=3$ 为可能达到的最大值。 $5$ 无法分成三个互不相等的正整数之,输出 `-1`。 --- 【数据范围】 **本题采用捆绑测试。** | 子任务编号 | $n\leq$ | 分值 | | :--------: | :-----: | :--: | | 1 | $50$ | 17 | | 2 | $500$ | 19 | | 3 | $10^5$ | 23 | | 4 | $10^9$ | 41 | 对于 $100\%$ 的数据,$1\le T \le 100$,$1\le n\le 10^9$。 这是我的代码, #include<bits/stdc++.h> using namespace std; int main() { int t; cin>>t; while(t--) { long long n; cin>>n; if(n<6) { cout<<-1<<endl; } else { long long p=1; for(long long i=6;i<sqrt(n);i++) { if(n%i==0) { p=n/i; break; } } if(p==1) { for(int i=1;i<6;i++) { if(n%i==0&&n/i>p&&n/i>=6) { p=i; } } } cout<<p<<endl; } } } 输入此数据 30 6 13 17 13 31 36 36 40 3 23 43 40 39 45 20 23 17 5 6 25 16 30 29 50 23 41 24 26 6 1
08-06
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值