2021ICPC昆明站(补题)

C.Cities

题意

给定n个城市,每个城市都有一个形状,你可以使用一次魔法把相同且连续的一段城市变成任意一个形状,问最少使用多少次魔法能使所有城市变成同一个形状。

题解
#include<bits/stdc++.h>
using namespace std;
const int N = 5e3 + 5, inf = 0x7f7f7f7f;
typedef long long ll;
int T, n, a[N], la[N], pre[N], dp[N][N];
int main()
{
    cin >> T;
    while(T--)
    {
    	cin >> n;
    	memset(pre, 0, sizeof pre);
		memset(la, 0, sizeof la);
		memset(dp, 0, sizeof dp);
    	for(int i = 1; i <= n; i++)
		{
			cin >> a[i];
			if(a[i] == a[i - 1]) i--, n--;
		}
    	for(int i = 1; i <= n; i++)
    	{
    		pre[i] = la[a[i]];
    		la[a[i]] = i;
		}
		for(int len = 2; len <= n; len++)
			for(int l = 1; l + len - 1 <= n; l++)
			{
				int r = l + len - 1;
				dp[l][r] = dp[l][r - 1] + 1;
				for(int k = pre[r]; k >= l; k = pre[k])
					dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r]);
				//cout << l << ' ' << r << "----" << dp[l][r] << endl;
			}
		cout << dp[1][n] << endl;
	}
    return 0;
}

J.Parallel Sort

题意

给定从1到n的一个全排列,通过最少轮交换使序列变为升序。
每一轮可以选择多组数对,但两组数对之间不允许有相同的,在这轮中会将这些位置上的数两两交换。并输出每轮交换的次数和要交换的两个数。

题解

每个位置上当前数可能有3种情况:

  1. 已经在它需要放到的位置上,无需交换。
  2. 它和它需要放到的位置上的数交换后,两个数都归回原位。
  3. 它和其他的多个数之间形成了一个环。如图:在这里插入图片描述
    只有两个数的小环可以直接在第一轮交换,但是大环则需要进行两轮交换才能全部归位。第一轮可以把大环拆解成多个小环,然后在第二轮直接交换小环中的数即可,因此最多只需要两轮。
    在这里插入图片描述
    交换之后:
    交换后
    这样大环就可以在第一轮交换后拆成小环了,我们设环中数的数量为m,则第一轮交换 ⌈ m 2 ⌉ \left\lceil\dfrac{m}{2}\right\rceil 2m次即可。
代码如下:
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<unordered_set>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int n, a[N], s[N], flag[N];
//s数组用来记录每个数当前所在位置,flag数组用来标记该数该轮被交换过
struct point
{
	int x, y;
};
vector<point> v;//记录需要交换的两个数
vector<int> b[N];//存放每一个大环中的数
void sp(int &x, int &y)//要将该数和其位置都交换
{
	swap(s[a[x]], s[a[y]]);
	swap(a[x], a[y]);
}
int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++)
    {
    	cin >> a[i];
    	s[a[i]] = i;
	}
	int flag1 = 0, p = 0;
	//flag1用来判断有无大环,p用来记下大环的个数
	for(int i = 1; i <= n; i++)
		if(a[i] != i && !flag[i])
		{
			int j = s[i], cnt = 0;
			if(j == a[i])//如果是小环就直接交换
			{
				v.push_back({i, j});
				flag[i] = flag[j] = 1;
				sp(i, j);
				continue;
			}
			flag1 = 1;
			b[++p].push_back(i);
			while(j != a[i])//找大环并存下来
			{
				flag[j] = 1;
				b[p].push_back(j);
				j = s[j];
			}
			b[p].push_back(j);
			flag[j] = 1;
		}
	if(!flag1)//无大环的情况
	{
		if(!v.size()) puts("0");//已经是升序了,则无需交换
		else
		{
			puts("1");
			int sz = v.size();
			cout << sz;
			for(int i = 0; i < sz; i++)
				cout << ' ' << v[i].x << ' ' << v[i].y;
		}
	}
	else//有大环的情况
	{
		puts("2");
		for(int i = 1; i <= p; i++)
		{
			int sz = b[i].size();
			for(int j = 0, k = sz - 1; j < k; j++, k--)
			{
				v.push_back({b[i][j], b[i][k]});//将大环第一轮要交换的数存入v数组中
				sp(b[i][j], b[i][k]);
			}
		}
		cout << v.size();
		for(int i = 0; i < v.size(); i++)
			cout << ' ' << v[i].x << ' ' << v[i].y;
		cout << endl;
		v.clear();//将v数组清空,进行第二轮交换
		for(int i = 1; i <= n; i++)
			if(a[i] != i)
			{
				int j = s[i];
				v.push_back({i, j});
				sp(i, j);
			}
		cout << v.size();
		for(int i = 0; i < v.size(); i++)
			cout << ' ' << v[i].x << ' ' << v[i].y;
	}
	return 0;
}

M.Stone Games

题意

给定一个长度为 n n n的序列,然后进行 q q q次询问,每次询问一个区间中不能用加法凑出的最小的正整数(询问强制在线)。

题解

先说结论:假设该区间所有数为 a 1 ≤ a 2 ≤ a 3 ≤ . . . ≤ a m a_1\leq a_2\leq a_3\leq... \leq a_m a1a2a3...am,答案用 a n s ans ans表示。若一个区间中没有 1 1 1,则 a n s = 1 ans=1 ans=1,否则 a n s = 1 + ∑ i = 1 j a i ans= 1+\sum_{i=1}^{j}a_i ans=1+i=1jai ( 1 ≤ j ≤ m ) (1\leq j\leq m) (1jm)
证明:若区间中没有 1 1 1,很显然 1 1 1就是答案。若有 1 1 1,我们假设用前 i i i个数可以凑出 [ 1 , x ] [1,x] [1,x],当 a i + 1 > x + 1 a_{i+1}>x+1 ai+1>x+1时,则有 x − a i + 1 < − 1 x-a_{i+1}<-1 xai+1<1,此时用已经凑出的数和 a i + 1 a_{i+1} ai+1无论怎样也凑不出 x + 1 x+1 x+1,当 a i + 1 ≤ x + 1 a_{i+1}\leq x+1 ai+1x+1时,则有 x ≥ x − a i + 1 + 1 ≥ 0 x\geq x-a_{i+1}+1\geq 0 xxai+1+10,此时用已经凑出的数和 a i + 1 a_{i+1} ai+1一定能凑出 [ 1 , x + a i + 1 ] [1,x+a_{i+1}] [1,x+ai+1]。由此可以推出 a n s = 1 + ∑ i = 1 j a i ans= 1+\sum_{i=1}^{j}a_i ans=1+i=1jai ( 1 ≤ j ≤ m ) (1\leq j\leq m) (1jm)
a n s ans ans就是该区间排序后的前缀和加 1 1 1,用主席树来维护区间和即可求出。
代码如下:

#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<unordered_map>

using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
int root[N], n, m, idx;
struct tree
{
	int l, r;
	ll sum;
}tr[N << 5];
int add(int p, int l, int r, int x)
{
	int u = ++idx, mid = l + r >> 1;
	tr[u] = tr[p], tr[u].sum += x;
	if(l < r)
	{
		if(x <= mid) tr[u].l = add(tr[p].l, l, mid, x);
		else tr[u].r = add(tr[p].r, mid + 1, r, x);
	}
	return u;
}
ll ask(int p, int u, int l, int r, ll x)
{
	if(l == r) return tr[u].sum - tr[p].sum;
	ll sum = tr[tr[u].l].sum - tr[tr[p].l].sum, mid = l + r >> 1;
	if(x <= mid) return ask(tr[p].l, tr[u].l, l, mid, x);
	else return ask(tr[p].r, tr[u].r, mid + 1, r, x) + sum;
}
int main()
{
	cin >> n >> m;
	int len = 1e9;
	for(int i = 1; i <= n; i++)
	{
		int x;
		cin >> x;
		root[i] = add(root[i - 1], 1, len, x);
	}
	ll res = 0;
	while(m--)
	{
		int l, r;
		cin >> l >> r;
		l = (l + res) % n + 1;
		r = (r + res) % n + 1;
		if(l > r) swap(l, r);
		res = 0;
		while(1)
		{
			ll t = ask(root[l - 1], root[r], 1, len, res + 1); //查询区间排序后的前缀和
			if(res == t) break;
			res = t;
		}
		cout << ++res << endl;
	}
	return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 2021年江西省大学生程序设计竞赛是一项针对大学生的编程竞赛活动。该竞赛旨在提高大学生的编程能力和创新思维,促进计算机科学技术的发展和应用。竞赛内容包括算法设计、程序实现、数据结构等方面,参赛选手需要在规定时间内完成指定的编程任务。这是一项非常有挑战性和有意义的竞赛,对于提高大学生的编程水平和实践能力有很大的帮助。 ### 回答2: 2021年3月14日,江西省大学生程序设计竞赛(ICPC)在江西师范大学举行。本次比赛由江西省计算机学会主办,吉林大学博爱学院赞助,共有15支队伍参赛,此次比赛的主要目的是提高学生们的程序设计与算法能力,同时为江西省选出优秀的程序设计人才。 比赛采用ACM国际竞赛的形式,共有12个题目,考察了参赛选手在算法设计、程序实现、数据结构、编程能力等方面的综合素质。比赛时间长达5个小时,选手需要在规定时间内完成尽可能多的题目,并在保证正确性的同时尽量节省时间。 2019年江苏省大学生程序设计竞赛的比赛题分别从算法设计基础、计算几何、动态规划、搜索、数据结构等方面出题,难度适中。所有参赛队伍均在赛场上充分展示了自己的才华和程序设计能力,赛场上紧张的气氛、激烈的角逐使得比赛更加刺激和有趣。 此次比赛从选拔、组织、管理等方面是非常成功的,既展示了江西省大学生程序设计的风采,也为江西省和国家培养了更多的优秀程序设计人才。希望在未来的竞赛中,江西省程序设计的水平能够更上一层楼,培养更多的优秀程序设计人才。 ### 回答3: 2021年江西省大学生程序设计竞赛于2021年11月7日在南昌大学体育馆举行,来自江西省内的30多所高校的近500支队伍参赛。本次比赛分为省赛和校内赛两个阶段,对参赛队伍进行初步筛选和终极评选。 比赛分为解决8道问题的组成员和4道问题的单人组成员两部分,涵盖了计算几何、动态规划、图论、计算几何等多个领域,难度较高。比赛中,每个队伍有5个小时的时间解决问题,只能在计算机上编写代码解决问题,考验了参赛者的编程实力和团队协作能力。 比赛期间,裁判们在场内设立了各种问题的试题,比如“手工奶酪”、“最大独立集”、“调色板”等,每个问题都要求参赛者在规定时间内精确解决。裁判们在以上操作的基础上,继续引入了随机性、收缩性和对称性等要素,使比赛题目更具挑战性。 本次比赛的成功举办,标志着江西省程序设计竞赛赛事的正式起航,并为江西省内高校的程序设计爱好者提供了一个互相交流、共同提高的平台。同时,也为将来江西省大学生程序设计竞赛的举办打下了坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值