2019洛谷csp冲刺模拟赛

难题做不动就去翻去年的洛谷csp题做,实在是水啊~

第一次

\(A\)

\(dp_{i, j}\)表示使用\(i\)个色子得到点数为\(j\)的概率,这样求出\(dp_x\)\(dp_y\)两个\(dp\)数组后就可以枚举\(x\)个色子得到的点数,\(y\)个色子得到的点数是一个前缀,枚举即可。

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

int x, y;

double dp[1050][6050], sum[6050];

int main()
{
	scanf("%d%d", &x, &y);
	for (int i = 1; i <= 6; i++) dp[1][i] = 1.0 / 6.0;
	for (int i = 1; i <= (max(x, y)) - 1; i++)
		for (int j = 1; j <= 6 * i; j++)
			for (int k = 1; k <= 6; k++)
				dp[i + 1][j + k] = dp[i + 1][j + k] + dp[i][j] / 6.0;
	double ans = 0, sum = 0;
	for (int i = 0; i <= x * 6; i++)
		ans += sum * dp[x][i], sum += dp[y][i];
	printf("%.2lf", ans * 100.0); putchar('%');
	return 0;
}

\(B\)

显然最后每个人都会到某个人站的地方集合。

那么先按每个人站的地方排序,从左往右枚举集合的位置,发现这个位置之前的人距离会增加,之后的人的距离会减少,转移是\(O(1)\)的。

所以瓶颈在于排序,时间复杂度\(O(n)\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

#define ll long long

const int N = 1000000;
const ll INF = 999999999999999999;

int a[N + 50], b[N + 50], n;

ll sumb[N + 50], sufb[N + 50];

struct Node
{
	int a, b;
} dl[N + 50];

void Read(int &x)
{
	x = 0; int p = 0; char st = getchar();
	while (st < '0' || st > '9') p = (st == '-'), st = getchar();
	while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
	x = p ? -x : x;
	return;
}

ll Abs(ll x)
{
	return x < 0 ? -x : x;
}

int Cmp(Node a, Node b)
{
	return a.a < b.a;
}

int main()
{
	Read(n);
	for (int i = 1; i <= n; i++) Read(dl[i].a);
	for (int i = 1; i <= n; i++) Read(dl[i].b);
	ll ans = INF, tmp = 0;
	sort(dl + 1, dl + n + 1, Cmp);
	for (int i = 1; i <= n; i++) sumb[i] = sumb[i - 1] + dl[i].b;
	for (int i = n; i >= 1; i--) sufb[i] = sufb[i + 1] + dl[i].b;
	for (int i = 1; i <= n; i++) tmp = tmp + 1LL * Abs(dl[i].a - dl[1].a) * dl[i].b;
	ans = tmp; 
	for (int i = 2; i <= n; i++)
	{
		tmp = tmp + 1LL * sumb[i - 1] * Abs(dl[i].a - dl[i - 1].a) - 1LL * sufb[i] * Abs(dl[i].a - dl[i - 1].a);
		ans = min(ans, tmp);
	}
	printf("%lld", ans);
	return 0;
}

\(C\)

对于一段函数,最大值最小值已知的情况下将这段函数拟合到\(min + (max - min) / 2\)是最优的。

所以问题转化为将数列分成一些段使得每段的极差尽可能小。

二分最小极差判断可行性。

发现判断可行性可以用倍增实现,注意这里倍增时不能算出总共需要的段数,而是段数大于给定段数就要退出。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>

using namespace std;

const int N = 1000000; 

int n, x[N + 50], y[N + 50], stmin[25][N + 50], stmax[25][N + 50], maxx;

void Read(int &x)
{
	x = 0; int p = 0; char st = getchar();
	while (st < '0' || st > '9') p = (st == '-'), st = getchar();
	while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
	x = p ? -x : x;
	return;
}

void Prework()
{
	maxx = 21;
	for (int j = 1; j <= maxx; j++)
		for (int i = 1; i + (1 << j) - 1 <= n; i++)
			stmin[j][i] = min(stmin[j - 1][i], stmin[j - 1][i + (1 << (j - 1))]),
			stmax[j][i] = max(stmax[j - 1][i], stmax[j - 1][i + (1 << (j - 1))]);
	return;
}

bool Check(int d, int m)
{
//	cout << d << endl;
	int l = 1, i;
	for (int i = 0; i < m && l <= n; i++)
	{
		int tmpmax = -1, tmpmin = 1000000001, r = l;
		for (int j = maxx; j >= 0; j--)
			if (r + (1 << j) - 1 <= n)
				if (max(tmpmax, stmax[j][r]) - min(tmpmin, stmin[j][r]) <= d)
					tmpmin = min(tmpmin, stmin[j][r]), tmpmax = max(tmpmax, stmax[j][r]), r += (1 << j);
	//	cout << l - 1 << " " ;
		l = r;
	}
//	cout << endl;
	return l == n + 1;
}

void Print(int x)
{
	if (x > 9) Print(x / 10);
	putchar(x % 10 + '0');
	return;
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) scanf("%*d%d", &stmin[0][i]), stmax[0][i] = stmin[0][i];
	Prework();
	int t, m;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d", &m);
		int l = 0, r = 1000000001;
		while (l < r)
		{
			int mid = (l + r) >> 1;
			if (Check(mid, m)) r = mid;
			else l = mid + 1; 
		}
        if (l % 2) printf("%d.5\n", l/2);
        else printf("%d\n", l/2);
	}
	return 0;
}
posted @ 2020-10-03 17:16  Tian-Xing  阅读(190)  评论(0编辑  收藏  举报