Codeforces Round 892 (Div. 2)A-D

啊啊啊啊啊啊,差一分钟交上D,个破区间合并想了一个多小时

A. United We Stand

题意:

给出一个数组a,把它分成两个数组b和c(b、c不能为空)使得c中的数不是b中的数的因子。不能则输出-1。

题解:

考虑把所有最小的数放进b,其他数放入c,这样就能绝对保证c中的数不是b中的数的因子。特别的当所有数的值相同时我们无法做到合法的分配。

#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
typedef pair PII;
const LL N = 1e2 + 10, MOD = 1e9 + 7, INF = 0x3f3f3f3f;
int a[N];
void solve()
{
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i)
		scanf("%d", &a[i]);
	sort(a + 1, a + 1 + n);
	if (a[1] == a[n])
	{
		printf("-1\n");
		return;
	}
	vectorb, c;
	for (int i = 1; i <= n; ++i)
	{
		if (a[i] == a[1])
			b.push_back(a[i]);
		else
			c.push_back(a[i]);
	}
	printf("%d %d\n", b.size(), c.size());
	for (auto i : b)
		printf("%d ", i);
	printf("\n");
	for (auto i : c)
		printf("%d ", i);
	printf("\n");
}
int main()
{
	int T = 1;
	scanf("%d", &T);
	while (T--)
	{
		solve();
	}
	return 0;
}

B. Olya and Game with Arrays

题意:

给出n(1<=n<=2.5e4)个数组,每个数组的长度为mi(2<=mi<=5e4)。定义这些数组的美丽值为每个数组中的最小值的和。

我们可以进行以下操作:选择一个数组中的一个元素并把它放入另一个数组,对于每个数组我们只能对它做一次取出操作,但我可以对它多次放入。

问进行若干次操作后这些数组的最大美丽值。

题解:

记每个数组的最小值为min1,次小值为min2,刚开始每个数组的最小值都min1,美丽值为eq?%5Csum%20min1,而当我们把最小值min1取出(先别管扔哪)它的最小值将会变为它的次小值min2,把所有的最小值扔掉之后我们的美丽值将会变成eq?%5Csum%20min2

我们再考虑我们需要把最大值扔到哪里,我们可以发现把min1全部扔到次小值min2最小的数组中会是最优的,这样操作之后会使得次小值最小的数组的最小值变为所有数组的最小值,记为minn,则最终答案为eq?%5Csum%20min2-min%28%5C%7Bmin2%5C%7D%29+minn

#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
typedef pair PII;
const LL N = 5e4 + 10, MOD = 1e9 + 7, INF = 0x3f3f3f3f;
int mn1[N], mn2[N];
void solve()
{
	int n, t = 1, minn = INF;
	scanf("%d", &n);
	for (int i = 1, m; i <= n; ++i)
	{
		scanf("%d", &m);
		mn1[i] = mn2[i] = INF;
		for (int j = 1, x; j <= m; ++j)
		{
			scanf("%d", &x);
			if (x <= mn1[i])
				mn2[i] = mn1[i], mn1[i] = x;
			else if (x < mn2[i])
				mn2[i] = x;
		}
		if (mn2[i] < mn2[t])
			t = i;
		minn = min(minn, mn1[i]);
	}
	if (n == 1)
	{
		printf("%d\n", minn);
		return;
	}
	LL ans = minn;
	for (int i = 1; i <= n; ++i)if (i != t)
		ans += mn2[i];
	printf("%lld\n", ans);
}
int main()
{
	int T = 1;
	scanf("%d", &T);
	while (T--)
	{
		solve();
	}
	return 0;
}

C. Another Permutation Problem

题意:

构造一个长度为n的排列,使得eq?%5Csum_%7Bi%3D1%7D%5E%7BN%7DAi*i-max%28%5C%7BAi*i%5C%7D%29最大,输出这个最大值。

题解:

我猜的...对于样例1排列为2,1,对于样例2排列为1,2,4,3,而对于n更大的序列发现值反转最后俩不够,试着枚举末尾反转的长度,A了!

#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
typedef pair PII;
const LL N = 5e4 + 10, MOD = 1e9 + 7, INF = 0x3f3f3f3f;
LL s[N];
LL get_s(int l, int r)
{
	LL res = 0, mx = 0;
	for (LL i = l, j = r; i <= r; ++i, --j)
	{
		res += i * j;
		mx = max(mx, i * j);
	}
	return res - mx;
}
void solve()
{
	int n;
	scanf("%d", &n);
	for (LL i = 1; i <= n; ++i)
		s[i] = s[i - 1] + i * i;
	LL ans = 0;
	for (int i = 1; i <= n; ++i)
		ans = max(ans, s[i - 1] + get_s(i, n));
	printf("%lld\n", ans);
}
int main()
{
	int T = 1;
	scanf("%d", &T);
	while (T--)
	{
		solve();
	}
	return 0;
}

D. Andrey and Escape from Capygrad

题意:

给出n个传送门:li,ri,ai,bi(li<=ai<=bi<=ri),如果你在区间li,ri之间,你可以传送到ai到bi之间的任意位置,你可以进行无数次传送。

给出q个询问:求从初始位置xi最远能到达的坐标值最大的位置

题解:

合并区间,我自己的思路太烦了还是不写了,写一半我自己都觉得我若至。

但我还是打算先放一个差点交上去的若至代码

正解在若至代码下边

#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
typedef pair PII;
const LL N = 2e5 + 10, MOD = 1e9 + 7, INF = 0x3f3f3f3f;
struct node
{
	int l, r, a, b;
	bool operator<(const node& x)const
	{
		return l < x.l;
	}
}v[N];
int p[N];
void solve()
{
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i)
	{
		scanf("%d%d%d%d", &v[i].l, &v[i].r, &v[i].a, &v[i].b);
	}
	sort(v + 1, v + 1 + n);
	for (int i = 1; i <= n; ++i)
	{
		int l = i, r = n;
		while (l < r)
		{
			int mid = l + r + 1 >> 1;
			if (v[mid].l <= v[i].b)
				l = mid;
			else
				r = mid - 1;
		}
		p[i] = l;
	}
	vectorvec;
	for (int i = 1; i <= n; ++i)
	{
		if (vec.size() && i <= vec.back().second)
			vec.back().second = max(vec.back().second, p[i]);
		else
			vec.push_back({ i,p[i] });
	}
	vectorl,r,ans;
	for (auto &i : vec)
	{
		int le = INF, ri = 0, mx = 0;
		for (int j = i.first; j <= i.second; ++j)
		{
			le = min(le, v[j].l);
			ri = max(ri, v[j].r);
			mx = max(mx, v[j].b);
		}
		l.push_back(le);
		r.push_back(ri);
		ans.push_back(mx);
	}
	int q;
	scanf("%d", &q);
	while (q--)
	{
		int x;
		scanf("%d", &x);
		int it = upper_bound(l.begin(), l.end(), x) - l.begin();
		if (it == 0)
		{
			printf("%d ", x);
			continue;
		}
		--it;
		if (r[it] < x)
		{
			printf("%d ", x);
			continue;
		}
		printf("%d ", max(x, ans[it]));
	}
	printf("\n");
}
int main()
{
	int T = 1;
	scanf("%d", &T);
	while (T--)
	{
		solve();
	}
	return 0;
}

对于每个传送门来说当我们在>bi的位置传送是没有意义的(反向逃跑了),所以对于每个传送门的ri是完全没有用的东西,我们只需要考虑当我们在区间li到bi时我们最远能到达bi即可。既我们只需要存下li与bi作为每个区间的左右边界。

我们对这些区间进行合并之后就可以得出继续若干次传送后,初始点在l到r的情况下最远能到r的所有区间。

对于每个询问我们可以二分查询他的所在区间或者离线遍历一遍所有区间去处理查询。

以下是智慧的代码

#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
typedef pair PII;
const LL N = 2e5 + 10, MOD = 1e9 + 7, INF = 0x3f3f3f3f;
void solve()
{
	int n;
	scanf("%d", &n);
	vectorv;
	for (int i = 1; i <= n; ++i)
	{
		int l, r, a, b;
		scanf("%d%d%d%d", &l, &r, &a, &b);
		v.push_back({ l,b });
	}
	sort(v.begin(), v.end());//随便整个区间合并板子
	vectorl, r;
	for (int i = 0; i < v.size();)
	{
		int x = v[i].first, y = v[i].second;
		while (i < v.size() && v[i].first <= y)
		{
			y = max(y, v[i].second);
			++i;
		}
		l.push_back(x);
		r.push_back(y);
	}
	int q;
	scanf("%d", &q);
	while (q--)
	{
		int x;
		scanf("%d", &x);
		int i = upper_bound(l.begin(), l.end(), x) - 1 - l.begin();
		if (i == -1 || r[i] <= x)//不能传送/不传送更优
			printf("%d ", x);
		else
			printf("%d ", r[i]);
	}
	printf("\n");
}
int main()
{
	int T = 1;
	scanf("%d", &T);
	while (T--)
	{
		solve();
	}
	return 0;
}

赛时没想到ri没用,不然肯定嘎嘎A,记录ri感觉这都快接近线段树的思路了...

你可能感兴趣的:(CF,算法)