26暑假训练团队题目
A - 扫雪
知识点:思维,贪心
思路:每个位置的雪可以往右或下移动,那么对于高出来的雪可以堆到一起处理,题意转换为把左上的雪移动到右下,也就是找出左上雪的最小值 \(mi\),用总和 \(sum-2*mi\)即为答案(注意 \(mi \ge 0\))
这里有一个易错点,左上雪不一定都是矩阵,只要所选取的右下雪无法移动到左上雪即可,每一行选中的格子数量,一定不能比上一行多。不难注意到左上雪呈倒阶梯状,所以可以用 \(dp\) 处理每一行,维护每一行后缀 \(dp\) 最小值,\(dp[i]\) 代表 这一行选了前 \(i\) 个格子,那么 通过 \(dp[j]=pre+surf[j]\) 即可计算出当前位置的最小左上雪。
Code
点击查看代码
void solve()
{
cin >> n >> m;
vi dp(m + 1, 0);
vi dp2(m + 1, 0);
vi a(m + 1, 0);
int sum = 0;
for (int i = 1; i <= n; i++)
{
a[m] = dp[m];
for (int j = m - 1; j >= 0; j--)
{
a[j] = min(a[j + 1], dp[j]);
}
int pre = 0;
dp2[0] = pre + a[0];
for (int j = 1; j <= m; j++)
{
int x;
cin >> x;
sum += x;
pre += x;
dp2[j] = pre + a[j];
}
dp = dp2;
}
int mi = dp[0];
for (int j = 1; j <= m; j++)
{
mi = min(dp[j], mi);
}
cout << sum - 2 * mi << endl;
}
B - 简单的构造题
知识点:构造,数学
思路:对于这么一个二维的数字矩阵构造,往往是不好考虑的,所以我们先考虑一维构造,若一个位置的数字为 \(i\) ,那么在下一个位置的数字一定不能是 \(i+1\) 或者 \(i+n-1\) ,也就是相邻位置的步长 \(K\) 的范围应该为 \(2<=K<=n-2\) 。那么我在 \(i\) 位置填 \((i*K)mod N\) ,那么左右位置就不会连续,再来看上下位置,对于 \(i\) 位置的下一行应为 \(i+m\) 位置,则他们的数字差为 \(K*m\) ,因为总长度为 \(n*m\),对它取模的结果也是 \(m\) 的倍数,也不会等于 \(1\) 或 \(n-1\),则构造成功 ,每个位置的数字应该为 \(((i * m + j) * K)mod (n*m)\) ,在 \(2\) 到 \(n*m-1\) 之间找一个互质的数 \(K\)即可。
Code
点击查看代码
void solve()
{
cin >> n >> m;
int nm = n * m;
if (nm == 3 || nm == 4)
{
cout << "No" << endl;
return;
}
if (nm == 6)
{
cout << "Yes" << endl;
if (n == 2 && m == 3)
{
cout << "1 3 5" << endl;
cout << "4 0 2" << endl;
}
else if (n == 3 && m == 2)
{
cout << "1 4" << endl;
cout << "3 0" << endl;
cout << "5 2" << endl;
}
else if (n == 1 && m == 6)
{
cout << "0 2 4 1 3 5" << endl;
}
else if (n == 6 && m == 1)
{
cout << "0\n2\n4\n1\n3\n5\n";
}
return;
}
int num = 2;
for (; num < nm - 1; num++)
{
if (gcd(num, nm) == 1)
{
break;
}
}
cout << "Yes" << endl;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
cout << ((i * m + j) * num) % nm << ' ';
}
cout << endl;
}
}
C - 国王游戏
知识点:排序,思维,高精度
思路:通过数学证明可以知道应该按 左手数与右手数的乘积进行排序,乘积越大,放在前面的损失就越大。
证明:求解 $$\dfrac{k}{b_1} + \dfrac{k\times a_1}{b_2} \ge \dfrac{k}{b_2} + \dfrac{k\times a_2}{b_1} $$
得证
由于数据范围太大,要采用高精度 \(\times\) 低精度 ,高精度 \(\div\) 低精度
Code
点击查看代码
struct node
{
int x, y;
};
bool cmp(node a, node b)
{
return a.x * a.y < b.x * b.y;
}
string div(string a, int b)
{
string res = "";
int num = 0;
bool ok = false;
for (char c : a)
{
num = num * 10 + (c - '0');
int e = num / b;
num %= b;
if (e > 0 || ok)
{
res += (char)(e + '0');
ok = true;
}
}
if (res.empty())
return "0";
return res;
}
string mul(string s, int b)
{
if (s == "0" || b == 0)
return "0";
string res = "";
int t = 0;
for (int i = s.size() - 1; i >= 0 || t; i--)
{
if (i >= 0)
t += (s[i] - '0') * b;
res += (char)(t % 10 + '0');
t /= 10;
}
reverse(res.begin(), res.end());
return res;
}
string maxn(string a, string b)
{
if (a.size() != b.size())
return a.size() > b.size() ? a : b;
return a > b ? a : b;
}
void solve()
{
int c1, c2;
cin >> n >> c1 >> c2;
vector<node> a(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> a[i].x >> a[i].y;
}
sort(a.begin() + 1, a.end(), cmp);
string sum = to_string(c1);
string ans = "0";
for (int i = 1; i <= n; i++)
{
string cnt = div(sum, a[i].y);
ans = maxn(ans, cnt);
sum = mul(sum, a[i].x);
}
cout << ans << endl;
}
D - Replacement
知识点:思维 ,贪心
思路:不难发现,我们所取得结果二进制串长度取决于左右两个串中较长得一个,所以左右某个串越长,我们猜测结果就是越大的。
若字符串每个字符均为 \(0\),那么结果是 \(0\);
当 \(s_0\) 为 \(1\) 时,选取 \(s_{n-2}\) 作为分界点是最好的答案,若同时 \(s_2\) 也为 \(1\),此时理论上的最大有效位数为 \(n-2\),那么显然我们只需要比较这两种情况哪一个结果最大即可
当字符串首个 \(1\) 出现的位置位于 \(s_2\)或之后,显然直接剪切后面所有字符即为最优秀答案
最复杂的情况是字符串首个 \(1\) 出现的位置位于 \(s_1\),此时理论上的最大有效位数为 \(n-3\),要选取 \(s_1,s_2,s_{n-2}\) 作为分界点进行比较
Code
点击查看代码
string work(string s, int i)
{
n = s.length();
int l1 = i;
int l2 = n - 1 - i;
int l3 = max(l1, l2);
string res(l3, '0');
for (int j = 0; j < l3; j++)
{
char c1 = '0', c2 = '0';
if (j < l1)
{
c1 = s[i - 1 - j];
}
if (j < l2)
{
c2 = s[n - 1 - j];
}
res[l3 - 1 - j] = ((c1 - '0') ^ (c2 - '0')) + '0';
}
int p1 = -1;
for (int j = 0; j < l3; j++)
{
if (res[j] == '1')
{
p1 = j;
break;
}
}
if (p1 == -1)
return "0";
return res.substr(p1);
}
bool cmp(string a, string b)
{
if (a.length() != b.length())
{
return a.length() > b.length();
}
return a > b;
}
void solve()
{
cin >> s;
n = s.length();
int k1 = -1;
for (int i = 0; i < n; i++)
{
if (s[i] == '1')
{
k1 = i;
break;
}
}
if (k1 == -1)
{
cout << "0" << endl;
return;
}
if (k1 == 0)
{
vi c;
c.push_back(n - 2);
if (s[2] == '1')
{
c.push_back(1);
}
string str = "";
for (int i : c)
{
string s1 = work(s, i);
if (str.empty() || cmp(s1, str))
{
str = s1;
}
}
cout << str << endl;
return;
}
if (k1 >= 2)
{
cout << s.substr(k1) << endl;
return;
}
if (s[2] == '1')
{
cout << s.substr(2) << endl;
return;
}
else
{
vi a = {1, 2, n - 2};
string str = "";
for (int i : a)
{
if (i >= 1 && i <= n - 2)
{
string s1 = work(s, i);
if (str.empty() || cmp(s1, str))
{
str = s1;
}
}
}
cout << str << endl;
}
}
E - 布置WAP
知识点:三分
思路:注意到,一定有个位置是最短的,此时无论往哪移动这个点,结果都会变大,也就是说这是一个单峰函数,考虑用三分。
同时我们可以采用换元的思路,将 \(x\) 或 \(y\) 作为 \(t\) 去推另外一个,这样坐标就可以用一个未知数来表示,对这个未知数进行三分,三分的范围题目已经给出。
同时要注意有可能除数为 \(0\),可以通过比较 \(|a|\)和 \(|b|\),来变换未知数防止除零错误。
Code
点击查看代码
struct node
{
double x, y;
};
vector<node> arr;
double a, b, c;
node point(double t)
{
if (abs(b) > abs(a))
{
double x = t;
double y = (-a * x - c) / b;
return {x, y};
}
else
{
double y = t;
double x = (-b * y - c) / a;
return {x, y};
}
}
double dis(double tx, double ty)
{
double mx = 0;
for (auto i : arr)
{
double dx = i.x - tx;
double dy = i.y - ty;
double d = dx * dx + dy * dy;
if (d > mx)
{
mx = d;
}
}
return sqrt(mx);
}
double cal(double t)
{
node i = point(t);
return dis(i.x, i.y);
}
void solve()
{
cin >> n;
arr.resize(n);
for (int i = 0; i < n; i++)
{
cin >> arr[i].x >> arr[i].y;
}
cin >> a >> b >> c;
double l = -4e4, r = 4e4;
int awa = 100;
while (awa--)
{
double lm = l + (r - l) / 3.0;
double rm = r - (r - l) / 3.0;
if (cal(lm) < cal(rm))
{
r = rm;
}
else
{
l = lm;
}
}
cout << fixed << setprecision(12) << cal((r + l) / 2.0) << endl;
}

浙公网安备 33010602011771号