暑假牛客多校第二场 2023-7-21(E、I、K、F、D)
E. Square
算法:二分
做法:我们依据x来枚举k,得到 \(x * 10^k\) ,用二分在[0, 1e9]查找mid的平方值,且这个值是第一个大于等于 \(x * 10^k\) 的值。得到这个值后我们再判断这个值在除 \(10^k\) 后是否与 \(x\) 相等即可。
code
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
#include <stack>
#include <deque>
#include <cmath>
#include <string>
#include <set>
#define fir first
#define sec second
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, int> PDI;
typedef pair<double, double> PDD;
int dx[4] = { -1, 1, 0, 0 }, dy[4] = { 0, 0, -1, 1 };
int getlen(LL x)
{
int cnt = 0;
while(x)x /= 10, cnt++;
return cnt;
}
LL sl(LL x)
{
LL l = 0, r = 1e9;
while(l < r)
{
LL mid = (l + r) / 2;
if(mid * mid >= x)r = mid;
else l = mid + 1;
}
return l;
}
LL sr(LL x)
{
LL l = 0, r = 1e9;
while(l < r)
{
LL mid = (l + r + 1) / 2;
if(mid * mid <= x)l = mid;
else r = mid - 1;
}
return r;
}
void solve()
{
LL x;
cin >> x;
if(x == 0)
{
cout << 0 << endl;
return;
}
int len = getlen(x);
for(int k = 0; k <= 19 - len ; k++)
{
LL p = 1;
for(int i = 1; i <= k ; i++)p = p * 10;
LL l = sl(x * p);
LL f = l * l;
for(int j = 1; j <= k ; j++)f /= 10;
if(f == x)
{
cout << l << endl;
return;
}
}
cout << "-1" << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
I. Link with Gomoku
算法:无
做法:这题是构造思维题,题目要求我们构造出x先下,o慢下的一种和棋。即任何一方都不赢。我们填表的时候每一次都填2 * m格且从左到右。第一次我们按xo、ox、xo......来填,其中第一个字母表示第每一个xo或ox的第一个字母表示填在第i行,第二个在第i + 1行。第二次是ox、xo、ox......
因此填写的次数是奇数次就是xo、ox、xo......,偶数次就是ox、xo、ox......最后我们特判最后依次如果我们不能以2 * m这样从左到右填写,那么我们就按xoxoxoxoxoxo......这样填好最后一行就好了。
code
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
#include <stack>
#include <deque>
#include <cmath>
#include <string>
#include <set>
#define fir first
#define sec second
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, int> PDI;
typedef pair<double, double> PDD;
int dx[4] = { -1, 1, 0, 0 }, dy[4] = { 0, 0, -1, 1 };
const int N = 1010;
char w[N][N];
void solve()
{
int n, m;
cin >> n >> m;
for (int i = 1, cnt = 1; i <= n ; i += 2, cnt++)
{
if (i + 1 > n)
{
for (int j = 1; j <= m; j++)
if (j % 2 != 0)w[i][j] = 'x';
else w[i][j] = 'o';
continue;
}
if (cnt % 2 != 0)
{
for (int j = 1; j <= m; j++)
{
if (j % 2 != 0)w[i][j] = 'x', w[i + 1][j] = 'o';
else w[i][j] = 'o', w[i + 1][j] = 'x';
}
}
else
{
for (int j = 1; j <= m; j++)
{
if (j % 2 != 0)w[i][j] = 'o', w[i + 1][j] = 'x';
else w[i][j] = 'x', w[i + 1][j] = 'o';
}
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)cout << w[i][j];
cout << endl;
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
K. Box
算法:动态规划
做法:建一个三维数组,第一维的i表示1 - i个box。第二维依据第三维的情况有所不同。
先说第三维,当第三维是0时表示当前第i个box没有盖子。当第三维维0时,第二维分两种情况,分别是0表示当前这个box没有盖子,1表示当前这个box一开始是没有盖子的,但是后来是有盖子的。
当第三维是1时表示第i个box起初是有盖子的。那么第二维我们划分为0、1、2,分别表示第i个box的盖子向左移,不移,向右移。
code
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
#include <stack>
#include <deque>
#include <cmath>
#include <string>
#include <set>
#define fir first
#define sec second
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, int> PDI;
typedef pair<double, double> PDD;
int dx[4] = { -1, 1, 0, 0 }, dy[4] = { 0, 0, -1, 1 };
const int N = 1000010;
int n;
LL a[N], b[N];
LL f[N][3][2];
LL max(LL a, LL b, LL c)
{
return max(max(a, b), c);
}
LL max(LL a, LL b, LL c, LL d)
{
return max(max(a, b), max(c, d));
}
LL max(LL a, LL b, LL c, LL d, LL e)
{
return max(a, max(b, c, d, e));
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++)cin >> a[i];
for (int i = 1; i <= n; i++)cin >> b[i];
for (int i = 1; i <= n; i++)
{
//不管第i - 1个box是有盖子还是无盖子我们都考虑进去,即第i - 1个box的第三维不管是1还是0我们都考虑。
if (b[i])
{
//第i - 1个box的盖子左移而且当前盖子不移,第i - 1个box的盖子不移且当前盖子也不移、第i - 1个box的盖子向右移且当前盖子不移、第i - 1个box的盖子无盖子且当前盖子不移、第i - 1个box的盖子本来无盖子但是有盖子移动到了第i - 1个box且当前盖子不动。
f[i][1][1] = max(f[i - 1][0][1] + a[i], f[i - 1][1][1] + a[i], f[i - 1][2][1], f[i - 1][0][0] + a[i], f[i - 1][1][0] + a[i]);//有盖子不移
//第i - 1个box的盖子左移而且当前盖子也左移、第i - 1个box的盖子不移且当前盖子左移、第i - 1个box的盖子无盖子且当前盖子左移、第i - 1个box的盖子本无盖子后来有盖子(由第i - 2个box的盖子得来)且当前盖子左移
//注意不能考虑f[i - 1][2][1] + a[i - 1]这种情况,这种情况下当第i - 1个box的盖子右移时,f[i - 1][2][1]是由 f[i - 1][2][1] = max(f[i - 2][0][1], f[i - 2][1][1], f[i - 2][2][1], f[i - 2][0][0], f[i - 2][1][0]) + a[i]; 转化而来,那么我们如果在第i - 1个box的盖子的右移情况中取的是f[i - 2][2][1] + a[i],那么这表示我们肯定是取了第i - 1个box的a值的,但是依据f[i - 1][2][1] + a[i - 1]来看我们又加了依次,显然矛盾。
f[i][0][1] = max(f[i - 1][0][1] + a[i - 1], f[i - 1][1][1], f[i - 1][0][0] + a[i - 1], f[i - 1][1][0]);//有盖子向左移
//第i - 1个box的盖子左移而且当前盖子右移、第i - 1个box的盖子不动且当前盖子右移、第i - 1个box的盖子右移而且当前盖子也右移、第i - 1个box无盖子而且当前盖子右移、第i - 1个box有盖子而且当前盖子右移
f[i][2][1] = max(f[i - 1][0][1], f[i - 1][1][1], f[i - 1][2][1], f[i - 1][0][0], f[i - 1][1][0]) + a[i + 1];//有盖子向右移
}
else
{
f[i][0][0] = max(f[i - 1][0][1], f[i - 1][1][1], f[i - 1][1][0], f[i - 1][0][0]);//无盖子并且没有移向当前的box
f[i][1][0] = f[i - 1][2][1];//无盖子但是有左边的盖子移过来,而且必定是第i - 1个box移过来的
, }
}
LL ans;
if(b[n])ans = max(f[n][0][1], f[n][1][1], f[n][2][1]);
else ans = max(f[n][1][0], f[n][0][0]);
cout << ans;
return 0;
}
F. Link with Chess Game
算法:二分图博弈论
二分图博弈结论:如果初始点在二分图中的最大匹配上,那么先手必胜,反之必败。
做法:首先考虑如果n是一个偶数的话,那么 \(n^3\) 是一个偶数,在三维的正方体中必定每个点都是最大匹配点,因此先手必胜。
如果n是一个奇数的话,那么 \(n^3\) 是一个奇数,那么我们需要根据初始的点来判断结果了。一开始的点有 \(r + g + b\) 为偶数的点,也有 \(r + g + b\) 为奇数的点,而我们知道在 \(n^3\) 且 \(n\) 为奇数的正方体中必定三点的和为奇数的点大于三点和为偶数的点。那么如果一开始点在三点和为偶数的点的话,先手移动一步后三点和必定为奇数。则可以得到 奇->偶->奇->偶......最终到最后一个奇数时这个奇数要变到偶数,但是偶数已经被遍历完了,则其必输,后手恰好是最后一步是奇数要变偶数,则先手必胜。若初始点和为奇数,我们也能稍微模拟一下。可以得到 偶->奇->偶->奇......倒数第二步一定是偶,最后一步是奇,随后奇没有可以走的点了。而先手是奇,则先手必败。
code
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
#include <stack>
#include <deque>
#include <cmath>
#include <string>
#include <set>
#define fir first
#define sec second
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, int> PDI;
typedef pair<double, double> PDD;
int dx[4] = { -1, 1, 0, 0 }, dy[4] = { 0, 0, -1, 1 };
const int N = 1000010;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--)
{
int n, r, g, b;
cin >> n >> r >> g >> b;
if (n % 2 == 0)cout << "Alice" << endl;
else
{
if ((r + g + b) % 2 == 0)cout << "Alice" << endl;
else cout << "Bob" << endl;
}
}
return 0;
}
D. The Game of Eating
算法:贪心
做法:我们若正着从1,2,3,...这样贪心会发现我们当前第i个人点菜的时候如果后面的人也会点这道菜,那么这个人就浪费了这次点菜的机会,不能使自己的点菜最大化。
如果倒着贪心。我们先令 \(t = m - k + 1\) 为最后一个人可以点的菜的数量,那么这个人一定会点他最喜欢的菜。那么在倒数第二个人点 \(t + 1\) 个菜的时候可以把最后一个人最喜欢的菜留给他,倒数第二个人点除了倒数第一个人最喜欢的菜之外他可以点的且是他最喜欢的菜。(其实我自己也没有很能理解这个贪心,我是看了这个博客来解释的,传送门)
code
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
#include <stack>
#include <deque>
#include <cmath>
#include <string>
#include <set>
#define fir first
#define sec second
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, int> PDI;
typedef pair<double, double> PDD;
int dx[4] = { -1, 1, 0, 0 }, dy[4] = { 0, 0, -1, 1 };
const int N = 2010;
int w[N][N];
int us[N];
void solve()
{
int n, m, k;
cin >> n >> m >> k;
memset(us, 0, sizeof(us));
vector<int> ans;
for(int i = 1; i <= n ; i++)
for(int j = 1; j <= m ; j++)
cin >> w[i][j];
for(int i = k; i >= 1 ; i--)
{
int p = i % n;
if(p == 0)p = n;
int t = -1;
for(int j = 1; j <= m ; j++)
{
if(!us[j] && (t == -1 || w[p][j] > w[p][t]))
t = j;
}
us[t] = 1;
ans.push_back(t);
}
sort(ans.begin(), ans.end());
for(auto it : ans)cout << it << ' ';
cout << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t;
cin >> t;
while(t -- )
{
solve();
}
return 0;
}

浙公网安备 33010602011771号