2021.5.11GRYZ模拟赛-数论专题
写在前面
啊啊啊啊 T3 结论假了假了假了假了假了。。。
我脑子过期了,弃了。。
所有题目详情可点击这里查看。
得分情况
题号 | 期望得分 | 实际得分 |
---|---|---|
\(T1\) | 100 | 100 |
\(T2\) | 30 | 0 |
\(T3\) | 100 | 0 |
总 | 230 | 100 |
T1
一道类似的题目:P1009 [NOIP1998 普及组] 阶乘之和
考场思路
直接计算阶乘值,然后加和输出结果。
注意到数据范围过大,应用高精度加法和乘法。
#include <cstdio>
#include <iostream>
#include <vector>
#include <iomanip>
#include <cassert>
#include <algorithm>
#include <cstring>
typedef long long LL;
using namespace std;
//*=========================================快读*/
inline LL read()
{
LL s = 1, w = 0;
char ch = getchar();
for (; !isdigit(ch); ch = getchar())
if (ch == '-')
s = -1;
for (; isdigit(ch); ch = getchar())
w = w * 10 + ch - '0';
return s * w;
}
//*=====================================定义变量*/
const int MARX = 1010;
const int Big_B = 10;
const int Big_L = 1;
inline int intcmp_(int a, int b)
{
if (a > b)
return 1;
return a < b ? -1 : 0;
}
struct Int
{
#define rg register
vector<int> c;
inline int max(int a, int b)
{
return a > b ? a : b;
}
inline int min(int a, int b)
{
return a < b ? a : b;
}
Int() {}
Int(int x)
{
for (; x > 0; c.push_back(x % Big_B), x /= Big_B)
;
}
Int(LL x)
{
for (; x > 0; c.push_back(x % Big_B), x /= Big_B)
;
}
inline void CrZ()
{
for (; !c.empty() && c.back() == 0; c.pop_back())
;
}
inline Int &operator+=(const Int &rhs)
{
c.resize(max(c.size(), rhs.c.size()));
rg int i, t = 0, S;
for (i = 0, S = rhs.c.size(); i < S; ++i)
c[i] += rhs.c[i] + t, t = c[i] >= Big_B, c[i] -= Big_B & (-t);
for (i = rhs.c.size(), S = c.size(); t && i < S; ++i)
c[i] += t, t = c[i] >= Big_B, c[i] -= Big_B & (-t);
if (t)
c.push_back(t);
return *this;
}
inline Int &operator-=(const Int &rhs)
{
c.resize(max(c.size(), rhs.c.size()));
rg int i, t = 0, S;
for (i = 0, S = rhs.c.size(); i < S; ++i)
c[i] -= rhs.c[i] + t, t = c[i] < 0, c[i] += Big_B & (-t);
for (i = rhs.c.size(), S = c.size(); t && i < S; ++i)
c[i] -= t, t = c[i] < 0, c[i] += Big_B & (-t);
CrZ();
return *this;
}
inline Int &operator*=(const Int &rhs)
{
rg int na = c.size(), i, j, S, ai;
c.resize(na + rhs.c.size());
LL t;
for (i = na - 1; i >= 0; --i)
{
ai = c[i], t = 0, c[i] = 0;
for (j = 0, S = rhs.c.size(); j < S; ++j)
{
t += c[i + j] + (LL)ai * rhs.c[j];
c[i + j] = t % Big_B, t /= Big_B;
}
for (j = rhs.c.size(), S = c.size(); t != 0 && i + j < S; ++j)
t += c[i + j], c[i + j] = t % Big_B, t /= Big_B;
assert(t == 0);
}
CrZ();
return *this;
}
inline Int &shlb(int l = 1)
{
if (c.empty())
return *this;
c.resize(c.size() + l);
rg int i;
for (i = c.size() - 1; i >= l; --i)
c[i] = c[i - l];
for (i = 0; i < l; ++i)
c[i] = 0;
return *this;
}
inline Int &shrb(int l = 1)
{
for (rg int i = 0; i < c.size() - l; ++i)
c[i] = c[i + l];
c.resize(max(c.size() - l, 0));
return *this;
}
inline Int div(const Int &rhs)
{
assert(!rhs.c.empty());
Int q, r;
rg int i;
if (rhs > *this)
return 0;
q.c.resize(c.size() - rhs.c.size() + 1);
rg int _l, _r, mid;
for (i = c.size() - 1; i > c.size() - rhs.c.size(); --i)
r.shlb(), r += c[i];
for (i = c.size() - rhs.c.size(); i >= 0; --i)
{
r.shlb();
r += c[i];
if (r.Comp(rhs) < 0)
q.c[i] = 0;
else
{
_l = 0, _r = Big_B;
for (; _l != _r;)
{
mid = _l + _r >> 1;
if ((rhs * mid).Comp(r) <= 0)
_l = mid + 1;
else
_r = mid;
}
q.c[i] = _l - 1, r -= rhs * q.c[i];
}
}
q.CrZ(), *this = r;
return q;
}
inline int Comp(const Int &rhs) const
{
if (c.size() != rhs.c.size())
return intcmp_(c.size(), rhs.c.size());
for (rg int i = c.size() - 1; i >= 0; --i)
if (c[i] != rhs.c[i])
return intcmp_(c[i], rhs.c[i]);
return 0;
}
friend inline Int operator+(const Int &lhs, const Int &rhs)
{
Int res = lhs;
return res += rhs;
}
inline friend Int operator-(const Int &lhs, const Int &rhs)
{
if (lhs < rhs)
{
putchar('-');
Int res = rhs;
return res -= lhs;
}
else
{
Int res = lhs;
return res -= rhs;
}
}
friend inline Int operator*(const Int &lhs, const Int &rhs)
{
Int res = lhs;
return res *= rhs;
}
friend inline std ::ostream &operator<<(std ::ostream &out, const Int &rhs)
{
if (rhs.c.size() == 0)
out << "0";
else
{
out << rhs.c.back();
for (rg int i = rhs.c.size() - 2; i >= 0; --i)
out << std ::setfill('0') << std ::setw(Big_L) << rhs.c[i];
}
return out;
}
friend inline std ::istream &operator>>(std ::istream &in, Int &rhs)
{
static char s[100000];
in >> s + 1;
int Len = strlen(s + 1);
int v = 0;
LL r = 0, p = 1;
for (rg int i = Len; i >= 1; --i)
{
++v;
r = r + (s[i] - '0') * p, p *= 10;
if (v == Big_L)
rhs.c.push_back(r), r = 0, v = 0, p = 1;
}
if (v != 0)
rhs.c.push_back(r);
return in;
}
friend inline bool operator<(const Int &lhs, const Int &rhs)
{
return lhs.Comp(rhs) < 0;
}
friend inline bool operator>(const Int &lhs, const Int &rhs)
{
return lhs.Comp(rhs) > 0;
}
#undef rg
} sum[100010], ans;
int n;
LL a[100010];
//*===================================自定义函数*/
//*=======================================主函数*/
signed main()
{
// freopen("a.in", "r", stdin);
// freopen("a.out", "w", stdout);
n = read();
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= n; i++)
{
sum[i] = 1;
for (int j = 1; j <= a[i]; j++)
sum[i] *= j;
ans += sum[i];
}
// for(int i=1;i<=n;i++)
// ans=ans+sum[i];
cout << ans;
return 0;
}
正解思路
吾即为真理。
#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
int read()
{
int 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;
}
int n;
struct B
{
int a[500], len;
B()
{
for (int i = 0; i < 500; i++)
a[i] = 0;
len = 0;
}
} fac[200];
B operator+(const B &a, const B &b)
{
B c;
c.len = max(a.len, b.len);
for (int i = 0; i <= c.len; i++)
c.a[i] = a.a[i] + b.a[i];
for (int i = 0; i <= c.len; i++)
if (c.a[i] >= 10)
{
c.a[i + 1]++;
c.a[i] -= 10;
}
if (c.a[c.len + 1])
c.len++;
return c;
}
B operator*(const B &a, const int &b)
{
B c;
c.len = a.len;
for (int i = 0; i <= c.len; i++)
c.a[i] = a.a[i] * b;
for (int i = 0; i <= c.len + 10; i++)
{
c.a[i + 1] += c.a[i] / 10;
c.a[i] %= 10;
}
for (int i = c.len + 10; i >= 0; i--)
if (c.a[i])
{
c.len = i;
break;
}
return c;
}
int main()
{
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
n = read();
fac[0].a[0] = 1;
for (int i = 1; i <= 100; i++)
fac[i] = fac[i - 1] * i;
B ans;
while (n--)
ans = ans + fac[read()];
for (int i = ans.len; i >= 0; i--)
printf("%d", ans.a[i]);
puts("");
}
T2
考场思路
没有思路。。。
一个半小时的时间推结论,发现只有 \(k \leq 2\) 时情况满足结论,果断特判想嫖走 \(30 pts\)。
果然还是太年轻了。。
一开始读错题?以为任意一列或任意一排不出现相同的颜色排列顺序才算合法,然鹅死活搞不出样例来,后来经过 K 神提醒才发现颜色组类不同就可以啦。
仍旧码不出来。。。
弃了弃了去看 \(T3\),临近交卷 \(10\) 分钟才悟到这原来是容斥
,容斥我也不会哇 (((以后上课再不睡觉了qwq
部分样例的手胡过程
正解思路
容斥。
枚举有 \(i\) 行 \(j\) 列同色的方案数,并乘上 \((-1)^{i+j}\) 加入答案。
令 \(f(i, j)\) 为 \(i\) 行 \(j\) 列同色,同色的行列所能取的颜色方案数。
\(i= 0,j= 0\) 时,\(f(i, j) = 1\)
\(i= 0, j >0\) 时,\(f(i, j) =k^j\)
\(i>0, j= 0\) 时,\(f(i, j)=k^i\)
\(i >0, j >0\) 时,\(f(i, j) =k\)
答案为
对于 \(i=0\) 或 \(j=0\) 的情况可以暴力计算,考虑 \(i\) 和 \(j\) 都大于 \(0\) 的情况。
就可以利用快速幂计算了。
#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);
return 0;
}
T3
考场思路
面向数据编程!!!
样例输入 1 10 6 2
,输出 53
拿出计算器一摁,发现 \(2^6 - (1+10) = 53\)
??????????
猜到了结论???
算了算了没时间了就这么交吧。。。祈祷不出错!!
果不其然是错的。。
/*
Name:
Solution:
By Frather_
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define int long long
#define InF 0x3f3f3f3f
#define kMod 998244353
using namespace std;
//*=========================================快读*/
int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
return x * f;
}
//*=====================================定义变量*/
int l,r,k,n;
//*===================================自定义函数*/
int Qpow(int a,int p)
{
int res = 1;
while(p)
{
if(p & 1)
res = res * a % kMod;
a = a * a % kMod;
p >>= 1;
}
return res;
}
//*=======================================主函数*/
signed main()
{
// freopen("c.in","r",stdin);
// freopen("c.out","w",stdout);
l=read();
r=read();
k=read();
n=read();
printf("%lld\n",(Qpow(n , k) - r - l)%kMod);
// fclose(stdin);
// fclose(stdout);
return 0;
}
正解思路
由裴蜀定理得,对于一个确定的 \(B\) ,答案为
由此,只需要对 \(L \sim R\) 中每个 \(B\) 计算上述公式的答案即可。
若记答案为 \(f(i)\) ,由于每个质因子独立,则其为积性函数。
如果 \(B\) 的质因数分解式中含有 \(p^{c}\) 这一项,那么单独考虑这一质因子,如果 \(K\) 的质因数分解式中含有 \(p^{d}\), 那么答案为 \(\sum \limits_{i_{1}=0}^{c} \sum \limits_{i_{2}=0}^{c} \cdots \sum \limits_{i_{n}=0}^{c} \left[ \min \left(i_{1}, i_{2}, \cdots, i_{n}, d\right)=0\right]\) 。
即如果 \(d>0\), 为\((c+1)^{n}-c^{n}\) ;否则为 \((c+1)^{n}\)
现在已经对所有质数的幂求出函数值了,利用师法,可以求出所有函数值。
#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 int N = 10001000;
const ll MOD = 998244353;
int L, R;
ll K, m;
ll mi[200];
int pr[N], a[N];
ll f[N];
ll pw(ll a, ll b)
{
ll re = 1;
while (b)
{
if (b & 1)
re = re * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return re;
}
int main()
{
freopen("c.in", "r", stdin);
freopen("c.out", "w", stdout);
L = read(), R = read(), K = read(), m = read();
for (int i = 0; i <= 100; i++)
mi[i] = pw(i, m);
for (int i = 2; i <= 10000000; i++)
{
if (!a[i])
{
pr[++pr[0]] = i;
a[i] = 1;
}
for (int j = 1; j <= pr[0] && 1LL * i * pr[j] <= 10000000; j++)
if (i % pr[j] == 0)
{
a[i * pr[j]] = a[i];
break;
}
else
a[i * pr[j]] = i;
}
for (int i = 1; i <= pr[0]; i++)
if (K % pr[i] == 0)
{
for (ll j = 1, t = pr[i]; t <= 10000000; j++, t *= pr[i])
f[t] = (mi[j + 1] - mi[j] + MOD) % MOD;
}
else
{
for (ll j = 1, t = pr[i]; t <= 10000000; j++, t *= pr[i])
f[t] = mi[j + 1];
}
f[1] = 1;
for (int i = 2; i <= 10000000; i++)
if (a[i] > 1)
f[i] = f[a[i]] * f[i / a[i]] % MOD;
for (int i = 2; i <= 10000000; i++)
f[i] = (f[i - 1] + f[i]) % MOD;
printf("%lld\n", (f[R] - f[L - 1] + MOD) % MOD);
}
写在最后
题目真的很有个性。
我没读懂 T2 ,另外二位没读懂 T3 ,题面是真的不错不错。