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)\)转移,转移方程:
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)\)个数
计算代价
换元
原本需要
由于
故