ACM ICPC 2013–2014, NEERC, Northern Subregional Contest

本文解析了Codeforces上两道竞赛题目,包括F.FlightBoardingOptimization和K-KidsinaFriendlyClass,详细介绍了使用树状数组和动态规划解决第一题的方法,以及利用Havel-Hakimi定理解决第二题的策略。

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

补下 F H K 吧。
题目连接 :https://codeforces.com/gym/100269

F. Flight Boarding Optimization

题意 : 给你一个序列,要求把他按数字的大小分成 k 块,比如数字大小1~ 4为一块,5~12为一块,每块在原序列的顺序对( i<j && a[i]<a[j] )的数量相加最小。

题解 : 我们可以用树状数组预处理出每个区间,他所能构成的顺序对数量。再用动态规划求解。

dp[i][j]定义为在数字大小为 i 时 切了 j 块

转移方程为 :dp[i][j] = min(dp[i][j], dp[q][j - 1] + num[q+1][i])(q从1到 i,num是区间的顺序对,从切了j-1转移过来)

AC代码:

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <string>
#include <vector>
#include <string>
#include <cmath>
#include <ctime>
#include <queue>
#include<stack>
#include <map>
#include <set>
#include<fstream>
#define lowbit(x) x&(-x)
using namespace std;
typedef  long long ll;
const int N = 1e3 + 5;
int a[N],c[N],num[N][N],dp[N][55];
vector<int>V[N];
int n, s, k;
void add(int i)
{
	while (i <= n) {
		c[i] ++;
		i += lowbit(i);
	}
}
int sum(int i)
{
	int ans = 0;
	while (i > 0) {
		ans += c[i];
		i -= lowbit(i);
	}
	return ans;
}
int main()
{
	freopen("flight.in", "r", stdin);
	freopen("flight.out", "w", stdout);
	memset(num, 0, sizeof(num));
	scanf("%d%d%d",&n,&s,&k);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
		V[a[i]].push_back(i);
	}
	for (int i = 1; i <= s; i++) {//树状数组预处理好每个区间的顺序对数量
		memset(c,0,sizeof(c));
		for (int j = i; j <= s; j++) {
			num[i][j] = num[i][j - 1];
			for (int k = 0; k < V[j].size(); k++) {
				num[i][j] += sum(V[j][k]);
			}
			for (int k = 0; k < V[j].size(); k++) {
				add(V[j][k]);
			}
		}
	}
	memset(dp, 0x3f, sizeof(dp));
	for (int i = 1; i <= s; i++)//初始化dp,切一次便是从1到 i 的顺序对了
		dp[i][1] = num[1][i];
	for (int i = 1; i <=s; i++) {
		for (int j = 2; j <k; j++) {
			for (int q =1; q <=i; q++) {
				dp[i][j] = min(dp[i][j], dp[q][j - 1] + num[q+1][i]);//转移方程
			}
		}
	}
	int ans =num[1][s];// k可能等于0,所以ans赋值为k等于0的情况,而且切越多会越小,所以也是最大值。
	for (int i = 1; i < s; i++) {//找切了k-1,也就是k块,再加上剩下的一块。
		ans = min(ans, dp[i][k - 1] + num[i+1][s]);
	}
	printf("%d\n", ans);
	return 0;
}

K - Kids in a Friendly Class

题意: 每个女生认识a个女生,b个男生,每个男生认识c个女生,d个男生,认识的人连边,问男生女生最少为多少,构成的图输出。

题解: 设女生有x人,男生有y人,可以得y=bx/c。所以x从c,y从b开始(题意至少得有这么多人)。bx=cy,所以b*(x+c/gcd(b,c))=c(y+b/gcd(b,c)),所以x,y每次加上对应得数后,看是否满足y>d && x>a &&!((x & 1) && (a & 1) || (y & 1) && (d & 1))(我们知道,每个点度数相加后%2=0,这些点才能连成一个图)
现在就是把图输出了,这时候就用到 Havel-Hakimi 定理,简单得说就是,度数先从大到小排序,度数最大(du)的点取出,与接下来的du个点连边,du这个点就不再用了,连边的点度数减一,若出现度数为负,则不能构成一个图。实现时可以变化一下,用优先队列维护度数最大,每次用度数最大与次大连边,次大减一放回队列,再取出次大,这样就行了。

AC代码:

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <string>
#include <vector>
#include <string>
#include <cmath>
#include <ctime>
#include <queue>
#include<stack>
#include <map>
#include <set>
#include<fstream>
#define lowbit(x) x&(-x)
using namespace std;
typedef  long long ll;
const int N = 1e6 + 5;
const int INF = 0x3f3f3f3f;
int a, b, c, d;
int gcd(int a, int b)
{
	return b ? gcd(b, a%b) : a;
}
void solve(int n, int m)
{
	priority_queue<pair<int, int> >girl;
	priority_queue<pair<int, int> >boy;
	for (int i = 1; i <= n; i++)
		girl.push(make_pair(a, i));
	while (!girl.empty())
	{
		pair<int, int> now = girl.top();
		girl.pop();
		for (int i = 0; i < now.first; i++)
		{
			pair<int, int> next = girl.top();
			girl.pop();
			printf("%d %d\n", now.second, next.second);
			next.first--;
			girl.push(next);
		}
	}
	for (int i = n + 1; i <= n + m; i++)
		boy.push(make_pair(d, i));
	while (!boy.empty())
	{
		pair<int, int> now = boy.top();
		boy.pop();
		for (int i = 0; i < now.first; i++)
		{
			pair<int, int> next = boy.top();
			boy.pop();
			printf("%d %d\n", now.second, next.second);
			next.first--;
			boy.push(next);
		}
	}
	for (int i = 1; i <= n; i++)
		girl.push(make_pair(b, i));
	for (int i = n + 1; i <= m + n; i++)
		boy.push(make_pair(c, i));
	while (!girl.empty())
	{
		pair<int, int> now = girl.top();
		girl.pop();
		for (int i = 0; i < now.first; i++)
		{
			pair<int, int> next = boy.top();
			boy.pop();
			printf("%d %d\n", now.second, next.second);
			next.first--;
			if (next.first != 0)boy.push(next);
		}
	}
}
int main()
{
	freopen("kids.in", "r", stdin);
	freopen("kids.out", "w", stdout);
	scanf("%d%d%d%d", &a, &b, &c, &d);
	int g = gcd(b, c);
	int dx = c / g, dy = b / g;
	int x,y;
	for (y = b, x = c; ; y += dy, x+=dx) {
		if(y>d && x>a &&!((x & 1) && (a & 1) || (y & 1) && (d & 1)))
			break;
	}
	printf("%d %d\n",x,y);
	solve(x,y);
}

H 待补

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值