5.11 考试解题报告

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

\(Link\)

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

\(Link\)

Solution

首先,这是一个容斥定理的题目,我们考虑用全集减去他的补集。

首先全集是很容易的出来的,就是 \(k ^ {n \times m}\),就是 \(n \times m\) 个点随便染色,根据乘法原理,显然成立。

之后考虑容斥,我们开始删去不合法的情况。

  • 当只有行不合法的时候

这个时候我们考虑删去 \(n\) 行中有 \(1\) 行为相同颜色的方案数,但是我们删去的话明显会删多,所以还要将两行为相同颜色的加上,这样反复,显然是个容斥定理,我们枚举删除的行数,从 \(i = 1 \sim n\),当 \(i\) 为奇数的时候就减去,否则加上。

那么公式是什么 :

考虑当前我们随机从 \(n\) 行中选了 \(i\) 行,所以是 \(\dbinom{n}{i}\)

之后我们选择的行中,因为每行都颜色相同,所以颜色共有 \(k ^ i\) 种情况。

剩下的矩形不就随便上颜色了,所以是 \(k ^ {(n - i) \times m}\) 种。

所以我们得到的式子就是 :

\[(-1) ^ i\dbinom{n}{i} \times k ^ i \times k ^ {(n - i) \times m} \]

  • 只有列不合法的时候

和上面的情况相同,也是容斥,最后得出来的式子长这样 :

\[(-1) ^ i \dbinom{m}{i} \times k ^ i \times k ^ {(m - i) \times n} \]

  • 行列都不符合的时候

依旧是一个容斥,不过比较复杂。

先从简单的情况开始,我们先从 1 行颜色相同并且 1 列颜色相同的情况开始。

这时候是从 \(n\) 中选出了 1 行和从 \(m\) 中选出了 1 列的时候,颜色有 \(k\) 种,并且其余的格子都是随便填的情况,即 :

\[k \times \dbinom{n}{1} \dbinom{m}{1} \times k ^ {(n - 1)(m - 1)} \]

由此推广到所有情况 :

\[Ans = k \sum_{i = 1} ^ n \sum_{j = 1} ^ m (-1) ^ {i + j}\dbinom{n}{i} \dbinom{m}{j} k ^ {(n - i)(m - j)} \]

发现 \(n,m \leq 10 ^ 6\),所以 \(O(n ^ 2)\) 枚举是肯定过不了的。

开始化简 :

\[\begin{aligned} Ans &= k \sum_{i = 1} ^ n \sum_{j = 1} ^ m (-1) ^ {i + j}\dbinom{n}{i} \dbinom{m}{j} k ^ {(n - i)(m - j)} \\ &= k \sum_{i = 1} ^ n \dbinom{n}{i} \sum_{j = 1} ^ m (-1) ^ {i + j} \dbinom{m}{j} k ^ {(n - i)(m - j)} \\ &= k \sum_{i = 1} ^ n \dbinom{n}{i} (1 + k ^ {n - i}) ^ m (-1) ^ {i} \\ &= k \sum_{i = 1} ^ n (-1) ^ {i}\dbinom{n}{i} (1 + k ^ {n - i}) ^ m \end{aligned} \]

中间第 \(2 \to 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\n", ans);
}

T3

\(Link\)

玄学题面,盯着看了半天都没看懂,甚至不知要求啥,所以到现在题解也没看懂,明天再看看。

posted @ 2021-05-11 22:20  Ti_Despairy  阅读(84)  评论(0)    收藏  举报