qbxt Day1

题目:

T1

皮蛋为了讨好黑妞,想要跟她打扑克。
他们打的扑克是这样一种规则:有面值大小从\(1\)\(n\)的扑克各一张。其中奇数牌在皮蛋手中,偶数牌在黑妞手中。每人每次只能出一张牌,先出完者获胜(遵循最基本的扑克规则:当对手出牌后,可以选择出一张比他大的牌,或者不管,让他再任意出一张牌)。假设皮蛋和黑妞都是足够聪明的人,都想让自己获胜。现在给定\(n\)和谁先出牌,那么谁会获胜呢?

解法:
当且仅当n是奇数且皮蛋先手,皮蛋才会获胜
特判\(n=2\)

反思

只得了\(20\) , 也特判了,但是特判写错了...

T2

皮蛋喜欢黑妞,想为她粉刷一面网格墙。

墙上有\(n\)\(m\)列共\(nm\)个网格。初始时,整面墙都是红色的。皮蛋只有红、蓝两种颜色的油漆,而且皮蛋很懒,他每次刷墙只会把某一整行或某一整列刷成红色或蓝色。皮蛋一共粉刷了\(k\)次墙。现在给出皮蛋的粉刷方案,请你求出最后这面墙有多少个格子是蓝色的。

解答

每个格子只会被最后一次粉刷影响,所以倒序考虑粉刷
行列之间的粉刷没有影响,每次粉刷可以直接算出粉刷了多少个格子

复杂度\(\Theta(n+m+k)\)

T3

一年一度的繁荣山庄直线竞速比赛要开始了!

本次比赛共有\(n\)位选手,第\(i\)位选手的起点在\(a_i\)位置,速度是\(v_i\),速度方向是正方向。

之后有\(Q\)个询问,每次询问经过\(t\)秒后,排名在第\(k\)位的选手的编号(在相同位置时,编号小的排名靠前)。

解法

暴力\(\Theta(nQ\log n)\)

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long

inline int read() {
	int res=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') {
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')res=res*10+ch-48,ch=getchar();
	return res*f;
}
const int maxn = 7005;
struct player {
	int a,v,id,now;
	bool operator<(const player &rhs)const{
		if(now==rhs.now)return id<rhs.id;
		return now>rhs.now;
	}
} p[maxn];
int n,q;
inline int query(int t,int k) {
	for(register int i = 1; i<=n; i++)
		p[i].now = p[i].a+p[i].v*t;
	sort(p+1,p+1+n);
	return p[k].id;
}
signed main() {
	n=read();
	for(register int i = 1; i<=n; i++) {
		p[i].v=read();
		p[i].id=i;
		p[i].a=read();
	}
	q=read();
	while(q--) {
		int t=read();
		int k=read();
		printf("%d\n",query(t,k));
	}


	return 0;
}

标准做法

我们考虑小学学过的追及问题,当两个人跑直线时,他们能且仅能相遇一次,所以他们距离的变化是单调的(可以是负距离)

那么我们可以离线处理询问,把询问按照时间排序,每次按照类似冒泡排序的方法维护序列.

复杂度\(\Theta(n^2+nQ+Q\log Q)\)

T4

有一个游戏:给定两个正整数序列\(A,B\),长度分别为\(n,m\)。你可以做如下操作:删掉\(A\)的最后\(x(x≥1)\)个数并得到它们的和\(S_1\),同时删掉BB的最后\(y(y≥1)\)个数并得到它们的和\(S_2\),这次操作的花费是\((S_1–x)(S_2–y)\)。你需要一直操作直到\(A,B\)都为空(不能做完一次操作后使得其中一个为空而另一个非空)。本次游戏的总花费是每次花费的和。求最小的总花费。

四十分暴力

考虑设计状态\(f(i,j)\)表示序列\(A,B\)分别还剩\(i,j\)个元素时的最小代价,枚举\(x,y\),从\(f(i+x,j+y)\)转移,转移方程:

\[f(i,j)= \min(f(i+x,j+y)+(S_1–x)(S_2–y)) \]

code

#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
	int res = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9')
	{
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9')
		res = res * 10 + ch - 48, ch = getchar();
	return res * f;
}
const int maxn = 2005;
int f[maxn][maxn];
int n, m;
int a[maxn], b[maxn];
signed main()
{
	n = read();
	m = read();
	for (register int i = 1; i <= n; i++)
		a[i] = a[i - 1] + read();
	for (register int i = 1; i <= m; i++)
		b[i] = b[i - 1] + read();
	memset(f, 0x7f, sizeof f);
	f[n][m] = 0;
	for (register int i = n; i >= 0; i--)
	{
		for (register int j = m; j >= 0; j--)
		{
			for (register int y = 1; y <= m - j; y++)
			{
				for (register int x = 1; x <= n - i; x++)
				{
					f[i][j] = min(f[i][j], f[i + x][j + y] + (a[i + x] - a[i] - x) * (b[j + y] - b[j] - y));
				}
			}
		}
	}
	cout << f[0][0];
	return 0;
}


正解

结论:存在一个最优解的每次删数,至少有一段长度是1

证明:设某一次删除\(A\)\(x\)个数,删除\(B\)\(y\)个数.

考虑将这一次操作分成两次,则分别删除\(A\)\(x_1,(x-x_1),\) \(B\)\(y_1,(y-y_1)\)个数

计算代价

\[(S_{x_1}-x_1 )( S_{y_1}-y_1)+( S_x-S_{x_1}-( x-x_1 ) ) ( S_y-S_{y_1}-( y-y_1 ) ) \]

\[=( S_{x_1}-x_1 ) ( S_{y_1}-y_1 ) +( ( S_x-x ) -( S_{x_1}-x_1 ) )( ( S_y-y ) -( S_{y_1}-y_1 ) ) \]

换元

\[= A\times B + (X-A)(Y-B) \]

\[=2 A B +XY-Y A - XB \]

原本需要

\[XY \]

由于

\[A\le X \text{且} B\le Y \]

\[2 A B -Y A - XB\le 0 \]

posted @ 2020-10-01 16:04  te5555  阅读(184)  评论(0)    收藏  举报