5.11 考试解题报告
前言
估分 : (100 + 30 + 0 = 130)。
实际得分 : (100 + 30 + 0 = 130)。
除了 (T1) 是个高精大水题之外都是不可做题,(T3) 连题目都没看懂。
第一次考试对着题目看了 20 分钟没看懂题目。
把考试的题目整理到题库里了,可以看了。
考场上的心路历程 :
- 开始看 (T1) 阶乘之和 ?大水题,写个高精就行了,写了 30 分钟,用计算器一测,没问题。
- 看了 (T2) 的第一眼,组合数学?希望自己能推出式子来。
- 20 分钟以后,好像还要用容斥?继续推。
- 30 分钟以后,这是什么神仙题目,算了,先把 (T3) 看看。
- 看了 10 分钟,题目是什么意思 ?连个样例解释都不给。
- 又看了 10 分钟,一脸懵逼,他这是要求什么 ?不管了,看 (T2)。
- 继续推 (T2),心中已经将 (T3) 放弃,题目都看不懂,又过了 20 分钟。
- (T2) 实在推不出来了,写爆搜吧,还有 30 分。
- 20 分钟后,写完了,一测样例,输出 0 ?有点小慌。
- 5 分钟后,哦,我的 dfs 没回溯,自己把自己逗笑了。
- 还有一些时间,再看看 (T3) 吧。
- 之后写了个 (O(?)) 的暴力,没过样例(输出差了老远),于是换了随机数。
- 对着 (T2) 又沉思了 (10) 分钟 + 看了看 (T1),交卷走人。
T1
Solution
- 前三十分,直接模拟即可。
- 后面 70 分,明显是要高精,也是直接模拟。
由于我对自己写的高精不放心,还是测试的分治了 /cy
注意高精乘的时候初始别忘附为 1。
#include <cstdio>
#include <cmath>
#include <iostream>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;
const int Maxk = 1e4 + 10;
LL a[110],Ans;
int n;
int maxn = -1;
struct Gao {
int z[Maxk];
int len_;
Gao () {
memset(z,0,sizeof z);
len_ = 0;
}
}A[102];
inline int Max(int a,int b) {return a > b ? a : b;}
inline int read()
{
int s = 0, f = 0;char ch = getchar();
while (!isdigit(ch)) f |= ch == '-', ch = getchar();
while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
return f ? -s : s;
}
Gao operator + (const Gao &c,const Gao &d)
{
Gao B;
B.len_ = Max(c.len_,d.len_) + 1;
for(int i = 1;i <= B.len_;i ++) {
B.z[i] += c.z[i] + d.z[i];
if(B.z[i] >= 10) {
B.z[i] -= 10;
B.z[i + 1] += 1;
}
}
while(B.len_ > 1 && B.z[B.len_] == 0) B.len_ --;
return B;
}
Gao operator * (const Gao &c,int nu)
{
Gao C,d;
int cnt = 0;
for(;nu;nu /= 10) {
d.z[++ cnt] = nu % 10;//这样会使变为倒序
}
d.len_ = cnt;
C.len_ = c.len_ + d.len_ + 1;
for(int i = 1;i <= c.len_;i ++) {
for(int j = 1;j <= d.len_;j ++) {
C.z[i + j - 1] += c.z[i] * d.z[j];
if(C.z[i + j - 1] >= 10) {
int k = C.z[i + j - 1] % 10;
C.z[i + j] += C.z[i + j - 1] / 10;
C.z[i + j - 1] = k;
}
}
}
while(C.len_ > 1 && C.z[C.len_] == 0) C.len_ --;
return C;
}
void Solve()
{
for(int i = 1;i <= n;i ++) {
LL sum = 1;
if(a[i] == 0) a[i] = 1;
for(int j = 1;j <= a[i];j ++) {
sum *= j;
}
Ans += sum;
}
cout << Ans << endl;
return;
}
signed main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
n = read();
for(int i = 1;i <= n;i ++) a[i] = read(),maxn = Max(maxn,a[i]);
if(maxn <= 10) {
Solve();
return 0;
}
for(int i = 1;i <= n;i ++) {
A[i].len_ = 1;
A[i].z[1] = 1;
}
Gao Sum;
for(int i = 1;i <= n;i ++) {
if(a[i] == 0) a[i] = 1;
for(int j = 1;j <= a[i];j ++) {
A[i] = A[i] * j;
}
Sum = Sum + A[i];
}
for(int i = Sum.len_;i >= 1;i --) cout << Sum.z[i];
return 0;
}
T1 连写带调用了 30 分钟,幸好没考高精减和高精除,两个都没学。
T2
Solution
首先,这是一个容斥定理的题目,我们考虑用全集减去他的补集。
首先全集是很容易的出来的,就是 (k ^ {n imes m}),就是 (n imes m) 个点随便染色,根据乘法原理,显然成立。
之后考虑容斥,我们开始删去不合法的情况。
- 当只有行不合法的时候
这个时候我们考虑删去 (n) 行中有 (1) 行为相同颜色的方案数,但是我们删去的话明显会删多,所以还要将两行为相同颜色的加上,这样反复,显然是个容斥定理,我们枚举删除的行数,从 (i = 1 sim n),当 (i) 为奇数的时候就减去,否则加上。
那么公式是什么 :
考虑当前我们随机从 (n) 行中选了 (i) 行,所以是 (dbinom{n}{i})。
之后我们选择的行中,因为每行都颜色相同,所以颜色共有 (k ^ i) 种情况。
剩下的矩形不就随便上颜色了,所以是 (k ^ {(n - i) imes m}) 种。
所以我们得到的式子就是 :
- 只有列不合法的时候
和上面的情况相同,也是容斥,最后得出来的式子长这样 :
- 行列都不符合的时候
依旧是一个容斥,不过比较复杂。
先从简单的情况开始,我们先从 1 行颜色相同并且 1 列颜色相同的情况开始。
这时候是从 (n) 中选出了 1 行和从 (m) 中选出了 1 列的时候,颜色有 (k) 种,并且其余的格子都是随便填的情况,即 :
由此推广到所有情况 :
发现 (n,m leq 10 ^ 6),所以 (O(n ^ 2)) 枚举是肯定过不了的。
开始化简 :
中间第 (2 o 3) 用了二项式定理。
所以我们就能过掉这个题目了,还没写,粘一下std。
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
typedef pair<int, int> PII;
#define fi first
#define se second
#define MP make_pair
ll read()
{
ll v = 0, f = 1;
char c = getchar();
while (c < 48 || 57 < c) {if (c == '-') f = -1; c = getchar();}
while (48 <= c && c <= 57) v = (v << 3) + v + v + c - 48, c = getchar();
return v * f;
}
const ll MOD = 998244353, MOD2 = MOD - 1;
const int N = 1010000;
ll n, m, K;
ll fac[N], inv[N];
ll pw(ll a, ll b)
{
a = (a % MOD + MOD) % MOD;
b = (b % MOD2 + MOD2) % MOD2;
ll re = 1;
while (b)
{
if (b & 1)
re = re * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return re;
}
ll C(int n, int m)
{
if (n < 0 || m < 0 || n - m < 0) return 0;
return fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}
int main()
{
//freopen("b.in", "r", stdin);
//freopen("b.out", "w", stdout);
fac[0] = 1;
for (ll i = 1; i <= 1000000; i++)
fac[i] = fac[i - 1] * i % MOD;
inv[0] = inv[1] = 1;
for (ll i = 2; i <= 1000000; i++)
inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
for (ll i = 1; i <= 1000000; i++)
inv[i] = inv[i - 1] * inv[i] % MOD;
n = read(), m = read(), K = read();
ll ans = pw(K, n * m);
for (ll i = 1; i <= n; i++)
if (i & 1)
ans = (ans - C(n, i) * pw(K, i) % MOD * pw(K, (n - i) * m) % MOD + MOD) % MOD;
else
ans = (ans + C(n, i) * pw(K, i) % MOD * pw(K, (n - i) * m) % MOD) % MOD;
for (ll i = 1; i <= m; i++)
if (i & 1)
ans = (ans - C(m, i) * pw(K, i) % MOD * pw(K, (m - i) * n) % MOD + MOD) % MOD;
else
ans = (ans + C(m, i) * pw(K, i) % MOD * pw(K, (m - i) * n) % MOD) % MOD;
for (ll i = 1; i <= n; i++)
if (i & 1)
ans = (ans - C(n, i) * K % MOD * ((pw(pw(K, n - i) - 1, m) - pw(K, (n - i) * m) + MOD) % MOD) % MOD + MOD) % MOD;
else
ans = (ans + C(n, i) * K % MOD * ((pw(pw(K, n - i) - 1, m) - pw(K, (n - i) * m) + MOD) % MOD) % MOD) % MOD;
printf("%lld
", ans);
}
T3
玄学题面,盯着看了半天都没看懂,甚至不知要求啥,所以到现在题解也没看懂,明天再看看。