4.14训练解题报告
Posted cxm1024的博客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了4.14训练解题报告相关的知识,希望对你有一定的参考价值。
比赛传送门 \\(\\colorwhite 20230413Tainrnig\\)
A. Ice Cave
题意:考虑糖豆人的蜂窝迷图中的一层,走过一个正常格子就会变成洞。给定当前地板局面(抽象成 \\(n\\times m\\) 矩阵),以及起点和终点,求是否能在终点位置掉到下一层。特殊地,本题不允许停留。起点终点可以相同。\\(n,m\\le 500\\)
判断是否能到达是容易的,dfs 即可。
如果终点本来有洞,到达终点即可。如果没有洞,到达终点后要向旁边走一格再回来。这也就要求旁边某个非洞的格子不能走。枚举邻居钦定成洞,判断是否可达即可。
对于起点等于终点的情况,只要将起点设成没有洞即可,避免了特殊处理。
By KrK
#include <cstdio>
#include <algorithm>
#pragma comment(linker, "/STACK:16000000")
using namespace std;
const int Maxn = 505;
const int Maxd = 4;
const int dy[Maxd] = -1, 0, 1, 0;
const int dx[Maxd] = 0, -1, 0, 1;
int n, m;
char B[Maxn][Maxn];
int sr, sc;
int fr, fc;
bool tk[Maxn][Maxn];
void Fill(int r, int c)
if (B[r][c] == \'X\' || tk[r][c]) return;
tk[r][c] = true;
for (int i = 0; i < Maxd; i++)
Fill(r + dy[i], c + dx[i]);
bool findPath()
fill((bool*)tk, (bool*)tk + Maxn * Maxn, false);
Fill(sr, sc);
return tk[fr][fc];
int main()
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%s", B[i] + 1);
B[i][0] = B[i][m + 1] = \'X\';
for (int j = 0; j <= m + 1; j++)
B[0][j] = B[n + 1][j] = \'X\';
scanf("%d %d", &sr, &sc); B[sr][sc] = \'.\';
scanf("%d %d", &fr, &fc);
if (B[fr][fc] == \'X\') B[fr][fc] = \'.\'; printf("%s\\n", findPath()? "YES": "NO"); return 0;
for (int i = 0; i < Maxd; i++)
int nr = fr + dy[i], nc = fc + dx[i];
if (B[nr][nc] == \'.\')
B[nr][nc] = \'X\';
if (findPath()) printf("YES\\n"); return 0;
B[nr][nc] = \'.\';
printf("NO\\n");
return 0;
B. Bad Luck Island
有三个种族,分别出剪刀石头布,每一时刻随机两个人相遇,输者会被吃掉(平局则无事)。问每个种族活到最后的概率。\\(n\\le 100\\)
显然可以 DP。设 \\(f[x][y][z]\\) 为一个三元组,表示在当前人数局面下,三个种族活到最后的概率。由于相同的无效,所以可钦定不同,人数一定会减少。简单算一下概率即可转移。
个人觉得此题中写记忆化搜索比较方便。
By cxm1024
#include <bits/stdc++.h>
using namespace std;
#define ld long double
array<ld, 3> f[110][110][110];
bool vis[110][110][110];
array<ld, 3> dfs(int x, int y, int z)
if (vis[x][y][z]) return f[x][y][z];
vis[x][y][z] = 0;
if (x == 0 || y == 0 || z == 0)
if (x && !z) return 1, 0, 0;
else if (y && !x) return 0, 1, 0;
else if (z && !y) return 0, 0, 1;
else assert(0);
ld all = x * y + y * z + x * z;
array<ld, 3> res0, 0, 0;
auto solve = [&](array<ld, 3> x, ld tmp)
for (int i = 0; i < 3; i++)
res[i] += x[i] * tmp;
;
solve(dfs(x - 1, y, z), (ld)x * z / all);
solve(dfs(x, y - 1, z), (ld)x * y / all);
solve(dfs(x, y, z - 1), (ld)y * z / all);
return f[x][y][z] = res;
signed main()
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
auto res = dfs(x, y, z);
printf("%.10Lf %.10Lf %.10Lf\\n", res[0], res[1], res[2]);
return 0;
C. Woodcutters
题意:数轴上有 \\(n\\) 棵树,坐标为 \\(x_i\\),高度为 \\(h_i\\)。砍倒时可以选择往左还是往右,但树之间不能重叠(点重叠也不行)。问最多砍多少棵树。
显然可以贪心。从左往右考虑每一棵树。如果可以往左倒则往左,否则如果可以往右就往右,否则不倒。过程中维护当前的右端点即可。正确性显然。
By cxm1024
#include <bits/stdc++.h>
using namespace std;
int n;
pair<int, int> a[100010];
signed main()
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d%d", &a[i].first, &a[i].second);
a[n + 1].first = 2e9 + 7;
int lst = -2e9, ans = 0;
for (int i = 1; i <= n; i++)
if (a[i].first - a[i].second > lst)
ans++, lst = a[i].first;
else if (a[i].first + a[i].second < a[i + 1].first)
ans++, lst = a[i].first + a[i].second;
else lst = a[i].first;
printf("%d\\n", ans);
return 0;
D. Queue
题意:有 \\(n\\) 个人,第 \\(i\\) 个人需要 \\(t_i\\) 时间服务,每个人希望等待时间不能超过他的服务时间。问最多能让多少人满意。\\(n,t\\le 10^5\\)
首先贪心地找性质。
-
首先发现不满意的人一定可以扔到最后,不会对其他人产生影响,所以只需要考虑选一部分人满意即可。
-
满意的人中一定是从小往大排,因为如果前面的时间大于后面的时间,后面的人一定不满意(等待时间过长),于是先从小到大排序。
-
进一步地,我们可以发现,每加入一个人,当前的时间都会至少 \\(\\times 2\\),所以人数是 \\(\\log\\) 级别的。
至此,有一个显然的 DP,\\(f[i][j]\\) 表示考虑了前 \\(i\\) 个人,选了 \\(j\\) 个的最小时间,则 \\(f[i][j]=a[i]+\\min f[k][j-1]\\),其中 \\(\\min\\) 式可以简单地维护。复杂度 \\(O(n\\log n)\\)。
By cxm1024
#include <bits/stdc++.h> using namespace std; #define int long long int a[100010], f[100010][35], g[35]; signed main() memset(g, 0x3f, sizeof(g)); g[0] = 0; int n; scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); sort(a + 1, a + n + 1); for (int i = 1; i <= n; i++) for (int j = 34; j >= 1; j--) if (g[j - 1] <= a[i]) f[i][j] = g[j - 1] + a[i]; g[j] = min(g[j], f[i][j]); int ans = 0; for (int i = 1; i <= 34; i++) if (g[i] < 1e18) ans = i; printf("%d\\n", ans); return 0;
-
继续观察可以发现,一定是每一步的人权值越小越好。即,如果某一次选择的人值为 \\(x\\),存在一个值为 \\(y(<x)\\) 的人也满足条件,此时换成选 \\(y\\) 一定更优。
据此,我们有一个更强的贪心的做法:排序后,从左到右考虑,如果能选则直接选中即可。
By KrK
#include <cstdio> #include <algorithm> using namespace std; typedef long long ll; const int Maxn = 100005; int n; int t[Maxn]; ll cur; int res; int main() scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%d", &t[i]); sort(t, t + n); for (int i = 0; i < n; i++) if (cur <= t[i]) res++; cur += t[i]; printf("%d\\n", res); return 0;
E. Soldier and Cards
题意:有两堆牌总共组成 \\(1\\sim n\\) 的排列,每次取顶上的比较,大的可以获取到对方的牌:先把对方的那张放到自己堆底,再把自己的那张放到堆底。没有牌的人输。问结束的步数以及输赢情况,或输出永远不会结束。\\(n\\le 10\\)。
注意到 \\(10!<4\\times 10^6\\),所以可以暴力模拟,如果出现环则永远不会结束,否则一定会在 \\(10!\\) 步内结束。
判定环看起来需要用 set 之类的东西,而且不方便记录状态(比如我就通过一个排列和一个分界点来表示状态)。事实上,只要暴力模拟,超过了 \\(10!\\) 步就可以认定为环。
By KrK
#include <cstdio>
#include <deque>
using namespace std;
const int lim = 4000000;
int n;
deque <int> Q1, Q2;
int res;
int main()
scanf("%d", &n);
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
int a; scanf("%d", &a);
Q1.push_back(a);
scanf("%d", &n);
for (int i = 0; i < n; i++)
int a; scanf("%d", &a);
Q2.push_back(a);
while (res <= lim && !Q1.empty() && !Q2.empty())
res++;
if (Q1.front() > Q2.front()) Q1.push_back(Q2.front()); Q1.push_back(Q1.front());
else Q2.push_back(Q1.front()); Q2.push_back(Q2.front());
Q1.pop_front(); Q2.pop_front();
if (res <= lim)
printf("%d %d\\n", res, Q1.empty()? 2: 1);
else printf("-1\\n");
return 0;
F. Soldier and Number Game
题意:多次询问,每次给定一个 \\(a,b\\),求 \\(\\fraca!b!\\) 的质因数个数(\\(p^c\\) 算 \\(c\\) 次)。\\(T\\le 10^6,a,b\\le 5e6\\)。
显然可以把分子分母分别求出来相减。由于 \\(n!\\) 的质因子个数即为 \\(1\\sim n\\) 的质因子个数之和,所以只需要求出每个值的质因子个数即可。可以通过线性筛/埃氏筛求出来。
By cxm1024
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5e6;
int prime[MAXN + 10], cnt[MAXN + 10], tot = 0;
bool isprime[MAXN + 10];
void getprime()
memset(isprime, 1, sizeof(isprime));
isprime[0] = isprime[1] = 0;
for (int i = 2; i <= MAXN; i++)
if (isprime[i])
prime[++tot] = i;
cnt[i] = 1;
for (int j = 1; j <= tot && i * prime[j] <= MAXN; j++)
isprime[i * prime[j]] = 0;
cnt[i * prime[j]] = cnt[i] + 1;
if (i % prime[j] == 0) break;
for (int i = 2; i <= MAXN; i++)
cnt[i] += cnt[i - 1];
void Solve(int test)
int x, y;
scanf("%d%d", &x, &y);
printf("%d\\n", cnt[x] - cnt[y]);
signed main()
getprime();
int T;
scanf("%d", &T);
for (int i = 1; i <= T; i++) Solve(i);
return 0;
G. Divisibility by Eight
题意:有一个数字串,找出一个子序列能被 \\(8\\) 整除,或判断无解。\\(n\\le 100\\)
我们知道,一个数能被 \\(8\\) 整除,当且仅当后三位是 \\(8\\) 的倍数。于是可以枚举选哪三位,复杂度 \\(O(n^3)\\)。注意不一定真的选三位,所以可以添加两个前导零(添加三个则可能选三个前导零,不合法)。
By cxm1024
#include <bits/stdc++.h>
using namespace std;
signed main()
string s;
cin >> s;
s = "00" + s;
for (int i = 0; i < s.size(); i++)
for (int j = i + 1; j < s.size(); j++)
for (int k = j + 1; k < s.size(); k++)
int x = (s[i] - \'0\') * 100 + (s[j] - \'0\') * 10 + (s[k] - \'0\');
if (x % 8 == 0)
printf("YES\\n%d\\n", x);
return 0;
puts("NO");
return 0;
同样可以数位 DP,记录当前选的位数,以及目前对 \\(8\\) 取模的结果。
By KrK
#include <cstdio>
#include <iostream>
#include <string>
using namespace std;
string s;
bool found;
void Print(int num)
printf("YES\\n");
printf("%d\\n", num);
void Gen(int lvl, int from, int has)
if (lvl && has % 8 == 0) Print(has); found = true;
if (lvl < 3)
for (int i = from; i < s.length() && !found; i++)
Gen(lvl + 1, i + 1, has * 10 + s[i] - \'0\');
int main()
cin >> s;
Gen(0, 0, 0);
if (!found) printf("NO\\n");
return 0;
H. Regular Bridge
题意:给定一个参数 \\(k\\),构造一个简单无向图,使得每个点的度数均为 \\(k\\),且图中存在割边。\\(k\\le 100\\)。
容易发现,\\(k\\) 为奇数时可如下构造:
\\(k\\) 为偶数时一定无解
- 证明 1(ysl):每个点的度数均为偶数,则一定存在欧拉回路,故存在环。
- 证明 2(cxm):若存在,一定能构造只有一条割边的哑铃状图(如上面奇数)。此时,考虑其中一侧,设有 \\(x\\) 个点,则总度数为 \\(xk\\),去掉割边后的子图度数和为 \\(xk-1\\)。而图的度数之和一定为偶数(边数等于度数之和的一半),所以不合法。
By cxm1024
#include <bits/stdc++.h>
using namespace std;
signed main()
int k;
scanf("%d", &k);
if (k % 2 == 0)
puts("NO");
return 0;
puts("YES");
printf("%d %d\\n", (k - 1) * 4 + 2, (k - 1) * (k - 1) * 2 + (k - 1) * 2 + 1 + k - 1);
auto add = [](int x, int y, int z)
printf("%d %d\\n", x * 2 - z, y * 2 - z);
;
auto solve = [&](int x)
for (int i = 2; i <= k; i++)
add(1, i, x);
for (int i = k + 1; i <= k + k - 1; i += 2)
add(i, i + 1, x);
for (int i = 2; i <= k; i++)
for (int j = k + 1; j <= k + k - 1; j++)
add(i, j, x);
;
solve(0), solve(1);
puts("1 2");
return 0;
I. Vanya and Scales
题意:有 \\(1,w,w^2,...\\) 的砝码每种一个,问能否在天平上称出重量为 \\(m\\) 的物品(注意砝码可以放在物品同侧)。
容易发现,转化成类似 \\(w\\) 进制的形式,每一位可以是 \\(-1,0,1\\) 三种取值,即“平衡 \\(k\\) 进制”。类似平衡三进制的构造方式,从低往高考虑,如果为 \\(0,1\\) 则可以直接放;如果为 \\(w-1\\) 则可以在同侧放一个,使其变为 \\(w\\) 并往上进位;否则无解。
By cxm1024
#include <bits/stdc++.h>
using namespace std;
signed main()
int w, m;
scanf("%d%d", &w, &m);
while (m > 0)
int tmp = m % w;
if (tmp == 0 || tmp == 1);
else if (tmp == w - 1) m++;
else
puts("NO");
return 0;
m /= w;
puts("YES");
return 0;
J. Vanya and Triangles
题意:给定平面上 \\(n\\) 个点,问有多少个三元组形成非退化三角形。\\(n\\le 2000,V\\le 100\\)
\\(n^3\\)
有 \\(n^3\\) 大暴力,我也不知道为什么可以过。
\\(n^2V\\)
转化一下,即为问有多少个三点共线。
有一个显然的 \\(O(n^2V)\\) 的算法,枚举两个点,然后枚举线上的所有坐标即可。
By cxm1024
#include <bits/stdc++.h>
using namespace std;
int gcd(int a, int b) return !b ? a : gcd(b, a % b);
int n, x[2010], y[2010];
bool vis[210][210];
signed main()
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d%d", &x[i], &y[i]);
x[i] += 101, y[i] += 101;
vis[x[i]][y[i]] = 1;
long long ans = 0;
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++)
int d = gcd(abs(x[i] - x[j]), abs(y[i] - y[j]));
int dx = (x[i] - x[j]) / d, dy = (y[i] - y[j]) / d;
int nowx = x[i], nowy = y[i];
while (1)
nowx += dx, nowy += dy;
if (nowx > 201 || nowy > 201 || nowx < 0 || nowy < 0)
break;
ans += vis[nowx][nowy];
nowx = x[i], nowy = y[i];
while (1)
nowx -= dx, nowy -= dy;
if (nowx > 201 || nowy > 201 || nowx < 0 || nowy < 0)
break;
ans += vis[nowx][nowy];
ans--;
ans /= 3;
printf("%lld\\n", (long long)n * (n - 1) * (n - 2) / 6 - ans);
return 0;
\\(n^2\\log n\\)
可以将枚举两个端点,\\(map\\) 统计直线的出现次数,如果某个直线上有 \\(k\\) 个点,则 map 里将出现 \\(\\frack(k-1)2\\) 次。通过出现次数反推出点数,进而得出三点共线数即可。
\\(n^2\\)
可以将斜率和截距哈希后使用 unordered_map 储存。
\\(n^2+V^2\\)
枚举一个端点,考虑算出其他点与他的斜率,如果有两个其他点到该点的斜率相同/相反,则产生一组三点共线。斜率可以用 \\(dx,dy\\) 约分后的二元组表示,值域很小可以直接用二维数组储存。
这种做法相较存直线的好处是,直线需要同时维护斜率和截距两个信息,且仅截距就可以达到 \\(V^2\\) 级别,极难简单地维护,不得不使用哈希等方式。而这种做法简单地使用二维数组即可。
By Um_nik
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 2020;
const int C = 204;
int gcd(int x, int y)
return (y == 0 ? x : gcd(y, x % y));
int x[N], y[N];
int a[C][2 * C];
int n;
ll ans;
void solve(int id)
memset(a, 0, sizeof a);
for (int i = id + 1; i < n; i++)
int xx = x[i] - x[id];
int yy = y[i] - y[id];
if (xx < 0)
xx *= -1;
yy *= -1;
if (xx == 0 && yy < 0)
yy *= -1;
int g = gcd(xx, abs(yy));
xx /= g;
yy /= g;
yy += C;
ans -= a[xx][yy];
a[xx][yy]++;
return;
int main()
cin >> n;
for (int i = 0; i < n; i++)
cin >> x[i] >> y[i];
ans = (ll)n * (n - 1) * (n - 2) / 6LL;
for (int i = 0; i < n; i++)
solve(i);
cout << ans << endl;
return 0;
K. Amr and Chemistry
题意:有 \\(n\\) 个数,每次操作可以选一个数左移/右移一位(二进制),问最少多少次能将他们变得相同。
考虑显然要找到他们二进制下的最长公共前缀,后面全部变成 \\(0\\)。假设前缀有 \\(x\\) 位,则找到 \\(x\\) 位后的第一个 \\(1\\),必然要通过右移将其消掉。每个数都这样操作后,变为公共前缀+若干个 \\(0\\)。将其剩余 \\(0\\) 的个数排序后,根据初中数学经典贪心,应该选中位数作为最终 \\(0\\) 的个数。
最长公共前缀使用可以先整体扫描做到 \\(n\\log n\\),但这里我为了好写暴力枚举的长度,本质没有区别。
By cxm1024
#include <bits/stdc++.h>
using namespace std;
int n, a[100010];
string s[100010];
signed main()
scanf("%d", &n);
int len = 1e5, ans = 1e9;
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
while (a[i] > 0) s[i] += a[i] % 2 + \'0\', a[i] /= 2;
len = min(len, (int)s[i].size());
for (int i = 1; i <= len; i++)
string t;
for (int j = s[1].size() - i; j < s[1].size(); j++)
t += s[1][j];
bool flag = 1;
for (int j = 2; j <= n; j++)
for (int k = 1; k <= i; k++)
if (s[j][s[j].size() - k] != t[t.size() - k])
flag = 0;
if (flag == 0) break;
if (flag == 0) break;
int res = 0;
vector<int> v;
for (int j = 1; j <= n; j++)
res += s[j].size() - i;
int k = s[j].size() - i - 1, tmp = 0;
for (; k >= 0; k--)
if (s[j][k] == \'0\') res--, tmp++;
else break;
v.push_back(i + tmp);
sort(v.begin(), v.end());
for (int j = 0; j < (v.size() - 1) / 2; j++)
res += v[(v.size() - 1) / 2] - v[j];
for (int j = (v.size() - 1) / 2; j < v.size(); j++)
res += v[j] - v[(v.size() - 1) / 2];
ans = min(ans, res);
printf("%d\\n", ans);
return 0;
L. Guess Your Way Out! II
题意:有一棵 \\(n\\) 层的满线段树,节点按照左儿子 \\(\\times 2\\)、右儿子 \\(\\times 2 + 1\\) 的顺序标号。有一个叶子为特殊点。给定 \\(q\\) 个信息,每个形如 \\(k,l,r,0/1\\),表示特殊点的第 \\(k\\) 层的祖先(即 \\(n-k\\) 次祖先)编号是否在 \\([l,r]\\) 内。保证 \\(l,r\\) 都是第 \\(k\\) 层的合法标号。你需要回答信息有冲突/有不止一个合法标号/输出特殊点标号。
做法一
\\(k\\) 次祖先在 \\([l,r]\\) 内,可以映射到特殊点在 \\([l+000...,r+111...]\\) 内(加号表示二进制后面添加若干位)。在 \\([l,r]\\) 外同理。于是现在有若干个限制,每个为在某区间内/在某区间外。于是可以离散化,每次将区间内/区间外+1,最后依次单点查询,如果被 \\(q\\) 个区间覆盖则全部满足。
实现上,可以用单点修改差分数组,最后求前缀和来简单地维护。最后查询答案时统计合法的个数以及任意一个合法解即可。个数为 \\(0\\) 则无解。
By cxm1024
#include <bits/stdc++.h>
using namespace std;
#define int long long
int v[100010], l[100010], r[100010], t[100010];
int b[200010], cnt = 0, s[200010];
signed main()
int h, q;
scanf("%lld%lld", &h, &q);
int ll = 1ll << (h - 1), rr = 1ll << h;
b[++cnt] = ll, b[++cnt] = rr;
vector<pair<int, int> > p;
for (int i = 1; i <= q; i++)
scanf("%lld%lld%lld%lld", &v[i], &l[i], &r[i], &t[i]);
l[i] <<= h - v[i], r[i] <<= h - v[i], r[i] += 1ll << (h - v[i]);
if (t[i]) p.push_back(l[i], r[i]);
else p.push_back(ll, l[i]), p.push_back(r[i], rr);
for (auto [x, y] : p) b[++cnt] = x, b[++cnt] = y;
sort(b + 1, b + cnt + 1);
cnt = unique(b + 1, b + cnt + 1) - b - 1;
for (auto [x, y] : p)
x = lower_bound(b + 1, b + cnt + 1, x) - b;
y = lower_bound(b + 1, b + cnt + 1, y) - b;
s[x]++, s[y]--;
for (int i = 1; i <= cnt; i++)
s[i] += s[i - 1];
int num = 0, ans = 0;
for (int i = 1; i < cnt; i++)
if (s[i] == q)
num += b[i + 1] - b[i];
ans = b[i];
if (num == 0) puts("Game cheated!");
else if (num >= 2) puts("Data not sufficient!");
else printf("%lld\\n", ans);
return 0;
做法二
同样是映射到叶子区间。考虑先处理在区间内的限制,再处理区间外的限制。
对于区间内,显然可以简单地区间求交来实现,最后得出答案的必要范围。
对于区间外,我们不能简单的直接实现,因为可能会打断区间的连续性,如下图:
此时合法区间分成了两段。这也是我赛时没有继续思考此思路的原因。
然而事实上,只需要将区间外的限制按左端点排序,从左往右考虑每个限制,即可保证区间的连续性。考虑使用归纳的思路,假设考虑完了前若干个限制,得到的合法区间为 \\([l,r]\\),现在考虑下一个限制 \\([x,y]\\)(外),则只有如上图,被严格包含的情况下才会分裂,否则直接缩短即可。
如果遇到了上图的情况,意味着什么呢?由于是按照左端点从左到右排序的,这就意味着 \\([l,x)\\) 这段区间永远不会被后面的限制变为不合法!于是可以直接将 \\([l,x)\\) 这段区间统计答案,并把 \\(l\\) 右移到 \\(y+1\\) 的位置即可。
By zerokugi
int h, q;
main()
ios::sync_with_stdio(0);
cin.tie(0);
scanf("%d%d", &h, &q);
ll l = 1ll << (h-1), r = (1ll << h);
vector<pll> rej;
REP(itr, q)
int i, t;
ll x, y;
scanf("%d%I64d%I64d%d", &i, &x, &y, &t);
x = x << (h-i);
y = (y+1) << (h-i);
if(t == 1)
l = max(l, x);
r = min(r, y);
else
rej.eb(x, y);
rej.eb(r, r);
sort(ALL(rej));
ll ans = -1;
for(auto it : rej)
ll x, y; tie(x, y) = it;
if(r <= l) break;
if(l < x)
if(ans != -1 || l + 1 < x)
cout << "Data not sufficient!" << endl;
return 0;
ans = l;
l = max(l, y);
if(ans == -1) cout << "Game cheated!" << endl;
else cout << ans << endl;
return 0;
这启示我们,当遇到看起来无法维护的信息时,不妨尝试将其排一下序,可能会有非常显著的效果。
[UOJ摸鱼]UOJ Easy Round #1解题报告
[UOJ摸鱼]UOJ Easy Round #1解题报告
前言
没组队训练的时候就只能自己来啦!
猜数
链接
题解
a,b都是g的倍数,然后n还是平方数。。那最小值就是两个乘数相等,最大值就是g+l啦。。
(Code)
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e5+10;
const int INF=1e9;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
return x*f;
}
void print(LL x){
if(x>9) print(x/10);
putchar(x%10+‘0‘);
}
LL g,l,x;
int main(){
int T;scanf("%d",&T);
while(T--){
scanf("%lld%lld",&g,&l);
x=l/g;
x=sqrt(x);
printf("%lld %lld
",(LL)2*x*g,g+l);
}
return 0;
}
以上是关于4.14训练解题报告的主要内容,如果未能解决你的问题,请参考以下文章
2020-3-14 acm训练联盟周赛Preliminaries for Benelux Algorithm Programming Contest 2019 解题报告+补题报告