The 10th Shandong Provincial Collegiate Programming Contest
A - Sekiro
题意
初始有\(n\)个金币,死了\(m\)次,死一次\(n = \lceil \frac n 2 \rceil\)。求最后的金币数。
思路
模拟。
代码
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
void solve()
{
int n, m;
cin >> n >> m;
while (m-- && n > 1)
{
n = (n + 1) >> 1;
}
cout << n << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
B - Median
题意
\(n\)个数(\(n\)是奇数),\(m\)对关系(\(u v\)表示\(u > v\)),对于第\(i\)个元素,给每个元素赋值,能使它成为中位数就输出\(1\),否则输出\(0\)。
思路
一条关系可以看作一条有向边,由大的指向小的,得到一张有向图,先判断有无环,有环说明情况不存在全为\(0\),可用\(bfs\)或\(dfs\)判断,同时记录该点的前继和后继,若该点为中位数,说明该点的前继和后继都不超过\(\frac n 2\)。
代码
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mxn = 110;
int n, m, cnt;
vector<int>g1[mxn], g2[mxn];
int up[mxn], low[mxn], deg[mxn], vis[mxn];
void dfs1(int u, int fa)
{
vis[u] = 1;
for (auto v : g2[u])
{
if (!vis[v])
{
cnt++;
dfs1(v, u);
}
}
}
void dfs2(int u, int fa)
{
vis[u] = 1;
for (auto v : g1[u])
{
if (!vis[v])
{
cnt++;
dfs2(v, u);
}
}
}
bool topo() {
cnt = 0;
queue<int>q;
for (int i = 1; i <= n; i++)
{
if (deg[i] == 0)
{
q.push(i);
}
}
while (!q.empty())
{
int u = q.front();
q.pop();
cnt++;
for (auto v : g1[u])
{
if (--deg[v] == 0)
{
q.push(v);
}
}
}
return cnt == n;
}
void init()
{
for (int i = 1; i <= n; i++)
{
g1[i].clear();
g2[i].clear();
deg[i] = 0;
up[i] = 0;
low[i] = 0;
}
}
void solve()
{
cin >> n >> m;
init();
for (int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v;
g1[u].push_back(v);
g2[v].push_back(u);
deg[v]++;
}
if (!topo())
{
for (int i = 1; i <= n; i++)
{
putchar('0');
}
putchar('\n');
return;
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
vis[j] = 0;
}
cnt = 0;
dfs1(i, -1);
up[i] = cnt;
for (int j = 1; j <= n; j++)
{
vis[j] = 0;
}
cnt = 0;
dfs2(i, -1);
low[i] = cnt;
}
for (int i = 1; i <= n; i++)
{
if (up[i] < (n + 1) / 2 && low[i] < (n + 1) / 2)
{
putchar('1');
}
else
{
putchar('0');
}
}
putchar('\n');
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
C - Tokens on the Segments
题意
\(n\)条线段,其中第\(i\)段的端点是\((l_i, i)\)和\((r_i, i)\),在平面的整数点放标记,但是标记的\(x\)坐标必须彼此不同。问最多有多少条线段上有标记。
思路
按如下方法将线段存入小根堆:左端点靠前的在前,左端点一样的,右端点靠前的在前。从最左边的线段的左端点开始计数,用指针记录当前位置。指针右移,同时更新其他线段的左端点(因为标记的\(x\)不能相同),到新的线段就\(ans++\),当前位置没有线段就直接移动到下一条线段。
代码
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
struct edge
{
int l, r;
bool operator < (const edge& a) const
{
if (l == a.l)
{
return r > a.r;
}
return l > a.l;
}
};
void solve()
{
int n;
cin >> n;
priority_queue<edge, vector<edge>> q; // 小根堆
for (int i = 0; i < n; i++)
{
int l, r;
cin >> l >> r;
q.push({ l,r });
}
int now = 1, ans = 0; // 当前可以标记的位置
while (!q.empty())
{
int l = q.top().l;
int r = q.top().r;
q.pop();
if (r >= now) // 右端点>=now就可以标记,且尽量往左标—,所以要更新左端点
{
ans++;
if (l < now) // 标记处在线段左边(与上一条之间有空)
{
now = l + 1;
}
else
{
now++;
}
}
while (q.size() && q.top().l == l) // 更新左端点
{
edge t = q.top();
q.pop();
t.l++; // 前推1
if (t.l <= t.r) // 仍然是线段(或者点)
{
q.push(t);
}
}
}
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
D - Stones in the Bucket
题意
\(n\)个桶,第\(i\)个桶里有\(a_i\)个石头。两种操作:从非空同种一走一个石头、把一个桶里的石头移到另一个桶里。问使得所有桶里石头数相同所需的最小操作数。
思路
模拟。
代码
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
void solve()
{
int n, sum = 0;
cin >> n;
vector<int> v(n);
for (int i = 0; i < n; i++)
{
cin >> v[i];
sum += v[i];
}
if (n == 1)
{
cout << 0 << endl;
return;
}
int ans = sum % n; // 剩下的
sum /= n; // 平均分
for (int i = 0; i < n; i++)
{
if (v[i] != sum && v[i] < sum) // 少的多的看一半就行
{
ans += (sum - v[i]);
}
}
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
F - Game on a Graph
题意
给定\(n\)个顶点\(m\)条边的简单连通图,\(k\)个人已知分队,轮流删边,使得图不连通的队伍输。两个队伍用最佳策略游戏,问哪队赢。
思路
连通图至少有\(n-1\)条边,也就是说谁能删\(m-(n-1)\)条边。
代码
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
void solve()
{
int n, m, k;
string s;
cin >> k >> s >> n >> m;
for (int i = 0; i < m; i++)
{
int u, v;
cin >> u >> v;
}
int idx = (m - n + 1) % k;
cout << (s[idx] == '1' ? '2' : '1') << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
H - Wandering Robot
题意
一个在\((0,0)\)机器人每次能够上/下/左/右移动一个单位,用\(U/D/L/R\)表示。已知长度为\(n\)的操作序列,以及重复操作了\(k\)次,求移动过程中机器人到点\((0,0)\)的曼哈顿距离的最大值。
曼哈顿距离:\(|x1−x2|+|y1−y2|\)。
思路
答案一定在第一次操作或第\(k\)次操作中,因为如果第一轮结束导致答案增大,那就会一直增大直至第\(k\)次操作结束;如果减小,之后也会一直减小。
代码
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n, k;
string cmd;
void move(int& x, int& y, int& ans)
{
for (int i = 0; i < n; i++)
{
switch (cmd[i])
{
case 'U':
y++;
break;
case 'D':
y--;
break;
case 'L':
x--;
break;
case 'R':
x++;
break;
default:
break;
}
ans = max(ans, abs(x) + abs(y));
}
}
void solve()
{
cin >> n >> k >> cmd;
int x = 0, y = 0, ans = 0;
move(x, y, ans);
int kx = (k - 1) * x, ky = (k - 1) * y; // (k - 1)次操作后的坐标
move(kx, ky, ans);
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
K - Happy Equation
题意
给定\(a\)、\(p\),问有多少个\(x\)满足\(a^x \equiv x^a \ mod \ 2^p\)。
思路
打表发现,当\(x\)为奇数时,答案为\(1\)。那就只用考虑\(x\)是偶数的情况:当\(x \le p\),直接枚举;当\(x > p\),此时\(a^x \ mod \ 2^p \equiv 0\),只要保证\(x^a \ mod \ 2^p = 0\)即可,即求\(2^p\)是\(2^{p/a}\)的多少倍。
代码
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
int qp(int base, int x, int mod)
{
int res = 1;
while (x)
{
if (x & 1)
{
res *= base;
res %= mod;
}
x >>= 1;
base *= base;
base %= mod;
}
return res;
}
void solve()
{
int a, p;
cin >> a >> p;
if (a & 1)
{
cout << 1 << endl;
return;
}
int mod = (1LL << p);
int ans = 0;
for (int i = 1; i <= p; i++)
{
if (qp(a, i, mod) == qp(i, a, mod))
{
ans++;
}
}
int t = p / a + (p % a != 0);
int l = 1LL << t;
ans += (mod / l - p / l);
cout << ans << endl;;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
M - Calandar
题意
定义一年有\(12\)个月,每月\(30\)天,每周\(5\)天(周五之后是周一)。已知今天是\(y_1\)年\(m_1\)月\(d_1\)日和周几,求\(y_2\)年\(m_2\)月\(d_2\)日是周几。
思路
算日期差值后取模即可。
代码
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
string week[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" };
void solve()
{
int y1, y2, m1, m2, d1, d2;
string w;
cin >> y1 >> m1 >> d1 >> w >> y2 >> m2 >> d2;
int num1 = y1 * 12 * 30 + m1 * 30 + d1;
int num2 = y2 * 12 * 30 + m2 * 30 + d2;
int dt = (num2 - num1) % 5;
int id = 0;
for (int i = 0; i < 5; i++)
{
if (w == week[i])
{
id = i;
break;
}
}
cout << week[(id + dt + 5) % 5] << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}