这一次的模拟赛一句话就是……考的不行……
对外宣称是NOIP的模拟赛,实际上难的……(可能因为我是蒟蒻的原因吧……)
一共6道题,55分(WTF?)
以下是题解……
鸥哨
【题解】
不难发现是一个组合数,于是就开始了一条不归路路——不断求阶乘,不断取模……最后妥妥地爆零!!!
为什么呢?——罪魁祸首就是因为一系列的不断取模……于是知道了一个神奇的“防爆”方法——逆元!
然后就可以通过逆元解决组合数爆炸的问题,最后不断更新答案就可以了!!!
什么是逆元?其实我也不清楚,但是并没有关系,只需要知道几件事情:
1、对于a、m,把同余方程中正整数x的最小值记作a模m的逆元;
2、逆元实际上就是用乘法代替除法;
3、用扩展欧几里得在算最大公约数的同时算出逆元(即:不定方程ax + my == 1的解x、y);
嗯……大抵上就是这些了,不懂扩展欧几里得的话百度吧……不想写了
【代码】
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <map>
#include <vector>
#include <queue>
#define L 110000
#define mod 1000000007
#define LL long long
using namespace std;
inline LL gi() {
char cj = getchar();
LL ans = 0, f = 1;
while (cj < '0' || cj > '9') {
if (cj == '-') f = -1; cj = getchar();
}
while (cj >= '0' && cj <= '9') ans = ans * 10 + cj - '0', cj = getchar();
return f * ans;
}
LL n, t, w, x, y;
LL f[L], inv[L], ans;
inline LL C (int n, int m) {
if (n < m) return 0;
LL res = (f[n] * inv[m]) % mod;
res = (res * inv[n - m]) % mod;
return res;
}
inline LL exgcd(LL a, LL b, LL &x, LL &y) {
if (b == 0) {
x = 1, y = 0; return a;
}
LL r = exgcd(b, a % b, x, y);
LL temp = x;
x = y, y = temp - (a / b) * y;
return r;
}
inline LL solve(LL a) {
LL x, y;
return exgcd(a, mod, x, y) == 1 ? (x + mod) % mod : -1;
}
int main() {
f[0] = 1;
for (LL i = 1; i <= 100000; ++i) f[i] = (i * f[i - 1]) % mod;
inv[0] = inv[1] = 1;
for (LL i = 2; i <= 100000; ++i) inv[i] = solve(i) % mod;
for (LL i = 2; i <= 100000; ++i) inv[i] = (inv[i] * inv[i - 1]) % mod;
n = gi(), t = gi(), w = gi();
for (int i = 1; i <= n; ++i) {
x = gi(), y = gi();
LL d = abs(x - w);
LL num = 0;
if (t % 2 == 1) {
if (d % 2 == 0) continue;
LL pos = (t + 1) / 2 + d / 2;
num = (ans + C(t, pos - 1)) % mod;
}
if (t % 2 == 0){
if (d % 2 == 1) continue;
LL pos = t / 2 + 1 + d / 2;
num = (num + C(t, pos - 1)) % mod;
}
num = (num * y) % mod;
ans = (ans + num) % mod;
}
printf("%lld\n", ans % mod);
return 0;
}
【总结】
这道题考试的时候天真的想着简单的杨辉三角,n^2的算法,最后好像还打萎了,部分分都没有拿全
通过这道题大概了解了一些扩展欧几里得求逆元,总之调了很久,才A的
骨牌移动
【题解】
暴力枚,具体看代码
【代码】
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <map>
#include <vector>
#include <queue>
#define mod 1000000009
#define L 10010
#define LL long long
using namespace std;
inline int gi() {
char cj = getchar();
int ans = 0, f = 1;
while (cj < '0' || cj > '9') {
if (cj == '-') f = -1;cj = getchar();
}
while (cj >= '0' && cj <= '9') ans = ans * 10 + cj - '0', cj = getchar();
return f * ans;
}
int n, m, k, a, b, c, d, x, y;
int p[12][L], ans;
bool vis[12][L], bj;
inline void dfs(int x, int y) {
if (!vis[x][y]) ans++, vis[x][y] = 1;
else return ;
if (y + 2 <= m && p[x][y + 1] == 1) {
p[x][y] = p[x][y + 1], p[x][y + 1] = p[x][y + 2];
dfs(x, y + 2);
p[x][y + 2] = p[x][y + 1], p[x][y + 1] = p[x][y];
}
if (y - 2 >= 1 && p[x][y - 1] == 2) {
p[x][y] = p[x][y - 1], p[x][y - 1] = p[x][y - 2];
dfs(x, y - 2);
p[x][y - 2] = p[x][y - 1], p[x][y - 1] = p[x][y];
}
if (x + 2 <= n && p[x + 1][y] == 3) {
p[x][y] = p[x + 1][y], p[x + 1][y] = p[x + 2][y];
dfs(x + 2, y);
p[x + 2][y] = p[x + 1][y], p[x + 1][y] = p[x][y];
}
if (x - 2 >= 1 && p[x - 1][y] == 4) {
p[x][y] = p[x - 1][y], p[x - 1][y] = p[x - 2][y];
dfs(x - 2, y);
p[x - 2][y] = p[x - 1][y], p[x - 1][y] = p[x][y];
}
}
int main() {
freopen("move.in", "r", stdin);
freopen("move.out", "w", stdout);
n = gi(), m = gi(), k = gi();
for (int i = 1; i <= k; ++i) {
a = gi(), b = gi(), c = gi(), d = gi();
if (a == c) {
if (b + 1 == d) p[a][b] = 1, p[c][d] = 2;
else p[a][b] = 2, p[c][d] = 1;
}
if (b == d) {
if (a + 1 == c) p[a][b] = 3, p[c][d] = 4;
else p[a][b] = 4, p[c][d] = 3;
}
}
for (x = 1; x <= n; ++x) {
for (y = 1; y <= m; ++y)
if (!p[x][y]) {bj = 1; break;}
if (bj) break;
}
dfs(x, y);
printf("%d\n", ans - 1);
return 0;
}
【总结】
考试的时候写别的题去了,并没有写这道题,随便输了一个值,最后果断的爆零了
看了一眼题解后,瞬间后悔考试的时候的斜体顺序,直接用数组记录了每个骨牌的方向,直接爆搜就A了,但是需要注意边界,因为边界WA了一次
围圈
【题解】
一道贪心的题
对于圈中的每一个数i,如果他的l[i]、r[i],满足l[i] > i且r[i] > i,那么一定是移除i,并且保留三个数中的最大数,显然是最优的,于是就会想到对于圈中每一个数进行排序,记录自己左边和右边的数,每一次把min(l[i], r[i]) - i加入ans即可
【代码】
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <map>
#include <vector>
#include <queue>
#define L 100000 + 1000
#define LL long long
using namespace std;
inline int gi() {
char cj = getchar();
int ans = 0, f = 1;
while (cj < '0' || cj > '9') {
if (cj == '-') f = -1;cj = getchar();
}
while (cj >= '0' && cj <= '9') ans = ans * 10 + cj - '0', cj = getchar();
return f * ans;
}
struct node {
int v, l, r;
}num[L];
LL n, ans, id[L];
inline bool comp (int a, int b) {
return num[a].v < num[b].v;
}
int main() {
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
n = gi();
for (int i = 1; i <= n; ++i) id[i] = i, num[i].v = gi(), num[i].l = i - 1, num[i].r = i + 1;
num[1].l = n, num[n].r = 1;
sort(id + 1, id + 1 + n, comp);
for (int i = 1; i < n; ++i) {
int ind = id[i];
ans += min(num[num[ind].l].v, num[num[ind].r].v) - num[ind].v;
num[num[ind].r].l = num[ind].l, num[num[ind].l].r = num[ind].r;
}
printf("%lld\n", ans);
return 0;
}
【总结】
这道题考试的时候本来是接近正解了的,但是可能是排序的方式不对(或者打萎了吧)最后并没有得到预期分数
拿到题解后,简单的看了一下发现了排序的问题,在原代码上做了微调就A了
难解的集合
【题解】
又是一道求逆元的题,在第一中已经用过了扩展欧几里得,所以这道题用一用费马小定理
费马小定理具体的内容很混乱(反正我没怎么懂),但是核心部分很简单:假如p是质数,且gcd(a,p)=1,那么 a(p-1)≡1(mod p)
对于这道题,不难发现满足组合数,不妨假设f(x)指向p,即最终所有的元素的函数值的函数值等于p
如果已经满足l个f(x)指向了p,那么显然是求从n - 1个数中取l - 1数的组合数,因为数p以确定被取,所以n,l都要减去1,而对于剩下的n - l个数也要在l - 1个数中取,于是将两个方案数相乘加入答案即可,但注意此时的答案是针对一个数的,最后有n个数,所以不要忘记乘以n
【代码】
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <map>
#include <vector>
#include <queue>
#define L 101000
#define LL long long
#define mod 1000000007
using namespace std;
inline LL gi() {
char cj = getchar();
int ans = 0, f = 1;
while (cj < '0' || cj > '9') {
if (cj == '-') f = -1;cj = getchar();
}
while (cj >= '0' && cj <= '9') ans = ans * 10 + cj - '0', cj = getchar();
return f * ans;
}
LL n, fac[L], ans;
inline LL inv(LL a, LL b) {
if (a < 0 || b < 0) return 0;
LL w = 1;
for (; b; b >>= 1, a = (LL)a * a % mod)
if (b & 1) w = (LL)w * a % mod;
return w;
}
int main() {
freopen("set.in", "r", stdin);
freopen("set.out", "w", stdout);
n = gi();
fac[0] = 1;
for (LL i = 1; i <= n; ++i) fac[i] = (LL)fac[i - 1] * i % mod;
for (LL l = 1; l <= n; ++l) {
LL C1 = (LL)fac[n - 1] * inv(fac[l - 1], mod - 2) % mod * inv(fac[n - l], mod - 2) % mod;
LL C2 = (LL)inv(l - 1, n - l);
ans = ans + (LL)C1 * C2 % mod, ans %= mod;
}
ans = (LL)ans * n % mod;
printf("%lld\n", ans);
return 0;
}
【总结】
对于这道题目,考试的时候并没有想到关于组合数、逆元、费马小定理之类一系列高大上的东西,于是就好不犹豫的打了一个n^n的表,最后20分
后面拿到题解后对于l分情况讨论不是很懂,于是就开始手玩(手玩是一个好东西啊),最后还是搞出来了,不过第一次用lemon测的时候ans忘记乘以n了,WA了一次,不过很快就调了出来