出题人题解
出题人题解, 若题解有修改,第一时间见: 传送门
继上一套练习赛,这一套卷子依旧是我和巨组队一起出的。然后还是我端茶倒水,然后帮巨完成了最后一块拼图(确信)。 我出了,然后Z巨出了。最后Z巨想出了题的的idea,我帮其完成了剩余的的数位DP思想找具体的那个区间的部分。
然后比赛那天的下午,跟验题人商量了一下临时加强了,范围从(原本甚至想),以至于赛时的难度
除了题过于经典,整套卷子质量海星,指的是海星(雾
A、切蛋糕的贝贝
题意:将一个正边形分成面积之比为最少需要几刀
因为,即判断是否为的倍数,如果不是,则无解。
是的话,则最少仅需刀即可达到目标,切法如下:
- 第一刀切穿过重心的对角线,左右两边
- 然后第二刀将左边的等分即切出
- 第刀,将右边的分为
#include <bits/stdc++.h>
using namespace std;
int n;
int main()
{
cin >> n;
cout << (n % 16 ? -1 : 5);
}
B、抱歉,这没有集美
题意:求解长度为的排列中,至少存在一个位置满足为偶数的排列数量。
本题理论上有相当一部分选手会使用打表,然后推出结论
逆向思维,考虑一个序列,不存在一个满足是偶数,也就是所任意偶数只能放在奇数位置,所以分开考虑奇数位置和偶数位置即可。然后我们对的奇偶性进行分类讨论:
- 当为奇数时,个奇数位置需要放个偶数和个奇数,我们从个数中选出一个奇数,然后两种位置分别全排列,总方案为
- 当为偶数时,两种位置分别全排列,总方案数为
故,不存在一个满足是偶数的排列方案有
综上,总的方案为,可以计算阶乘求解答案。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
typedef long long LL;
constexpr int N = 1e6 + 10, md = 1e9 + 7;
LL fac[N];
void init()
{
fac[0] = 1;
rep(i, 1, 1000000)
fac[i] = fac[i - 1] * i % md;
}
int main()
{
int i;
cin >> i;
init();
LL ans = fac[i] - fac[(i + 1) / 2] * fac[(i + 1) / 2] % md;
ans %= md;
if(ans < 0)
ans += md;
cout << ans;
}
C、打牌的贝贝
题意: 两人都持有张牌,且每张牌的数字都不同。共回合,每轮都是攻击,防守。每回合出一张牌,必须出一张比当前回合他出的大的牌。如果轮都成功防御则其获胜,反之获胜。共组样例,输出二人各自获胜的情况种类。
首先声明下,本题不是博弈题,这不是二者轮流操作的公平组合游戏。
容易得到,先手打出一张牌时候,后手一定会打出他目前已有的手牌中,最小的大于的牌。
先考虑给定时,共有多少种分牌方案,即在个位置中选取出个位置当作先手的牌(位置的编号即是大小,也就已经排序好了),剩余的位置即是后手的牌,即总共的方案数量为。考虑后手获胜的情况,我们把先手的牌所处的位置,都换成左括号,后手的牌的位置都换成右括号。这样我们就得到了一个括号序列,当且仅当这是一个合法的括号序列时,后手才能获胜,故后手获胜的情况为卡特兰数,易得卡特兰数可以预处理出来,然后询问回答。
于是就得到了先手获胜的方案数为,我们通过递推式可以计算出卡特兰数。再此之前,我们需要计算出的逆元,这个先预处理阶乘和阶乘的逆元,然后求解,而这个数也可以由的阶乘和的阶乘的逆元计算出来。
当然用卡特兰数的这个递推式也是同样的效果。
总的时间复杂度为。出题人认为阶乘逆元的预处理复杂度不是该题想考查的重点,故处理阶乘逆元的复杂度放宽到也能通过,即最大仅为。
#include <bits/stdc++.h>
using namespace std;
#define el '\n'
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define lop(i, a, b) for (int i = (a); i < (b); i++)
#define dwn(i, a, b) for (int i = (a); i >= (b); i--)
typedef long long LL;
constexpr int N = 1e6 + 10, md = 1e9 + 7;
int T, n, m;
LL f[N], fac[2 * N], ifac[N], inv[N];
LL fpow(LL a, int b)
{
LL res = 1;
while(b)
{
if(b & 1)
res = res * a % md;
a = a * a % md;
b >>= 1;
}
return res;
}
LL finv(int i)
{
return fpow(i, md - 2);
}
void init()
{
int R = N - 8; // 1e6 + 2
fac[0] = 1;
rep(i, 1, 2 * R) //阶乘逆元
fac[i] = fac[i - 1] * i % md;
ifac[R] = fpow(fac[R], md - 2);
dwn(i, R, 2) //阶乘倒数逆元
ifac[i - 1] = ifac[i] * i % md;
rep(i, 1, R) //倒数逆元
inv[i] = fac[i - 1] * ifac[i] % md;
f[0] = 1;
R -= 2;//最大的i + 2也被算出来了
lop(i, 0, R)
{ //计算卡特兰数
f[i + 1] = f[i] * (4 * i + 2) % md * inv[i + 2] % md;
//这是一个很坑的点i+2
}
}
LL C(LL n, LL m)
{//n选m
return fac[n] * ifac[m] % md * ifac[n - m] % md;
}
int main()
{
cin.tie(0);
cout.tie(0);
cin.sync_with_stdio(false);
init();
cin >> T;
while (T--)
{
cin >> n;
LL ans1 = ((C(2 * n, n) - f[n]) % md + md) % md;
cout << ans1 << ' ' << f[n] << el;
}
}
D、点分治分点
题意:给定一个 个点, 条带权的边的有向图,定义一条简单路径的 值为其路径上的边权的最小值, 为从 到 所有简单路径的最大 值。对于给定的, 从 到 输出 ,如果没有任何一条简单路径则输出 。
-
我们先把边按照权值排序,从大到小加边。
-
每次加一条边 ,如果 与 连通,我们就从 开始跑一次 ,对于在该次 中连通的点,它的答案就是 。
-
我们对每个跑过的点打个标记,由于每个点每条边都只会被遍历一次,因此我们计算答案的复杂度为
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 1e5 + 10, M = 1e7;
int vis[N], ans[N]; // 答案
vector<int> g[N]; // 存边
struct Edge {
int u, v, w;
bool operator < (const Edge &t) const {
return w > t.w;
}
} E[N];
void bfs(int s, int w)
{
vis[s] = 1;
ans[s] = w;
queue<int> q;
q.push(s);
while(q.size())
{
int u = q.front(); q.pop();
for (auto &v : g[u])
{
if(vis[v]) continue;
vis[v] = 1;
ans[v] = w;
q.push(v);
}
}
}
void solve()
{
int n, m, s; // n个点, m条边
cin >> n >> m >> s;
// n = m = 1e5,s=1;
for (int i = 1; i <= m; i ++ )
{
int u, v, w;
cin >> u >> v >> w;
// u=1,v=i,w=i;
E[i] = {u, v, w};
}
sort(E + 1, E + m + 1); // 按照边权降序
vis[s] = 1;
memset(ans, -1, sizeof ans);
for (int i = 1; i <= m; i ++ )
{
auto &[u, v, w] = E[i];
g[u].push_back(v); // 加边
// 寻找是否拥有新可达点
if(vis[u] == 1 && vis[v] == 0) bfs(v, w);
}
ans[s] = -1;
for (int i = 1; i <= n; i ++ )
cout << ans[i] << " \n"[i == n];
}
int main()
{
// ios::sync_with_stdio(0), cin.tie(0);
int t = 1;
while (t -- ) solve();
return 0;
}
E、角剖分剖角
牛客编辑器有字数上限,剩余部分见知乎: 传送门
F、胖头鱼头胖
牛客编辑器有字数上限,剩余部分见知乎: 传送门