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)

答案为

[sum limits_{i=0}^{n} sum limits_{j=0}^{m} (-1)^{i+j} C_{n}^{i} C_{m}^{j} cdot f(i, j) cdot k^{(n-i)(m-j)} ]

对于 (i=0)(j=0) 的情况可以暴力计算,考虑 (i)(j) 都大于 (0) 的情况。

[egin{aligned} &k sum_{i=1}^{n} sum_{j=1}^{m}(-1)^{i+j} C_{n}^{i} C_{m}^{j} cdot k^{(n-i)(m-j)} \ = &k sum_{i=1}^{n}(-1)^{i} C_{n}^{i} sum_{j=1}^{m}(-1)^{j} C_{m}^{j} cdot k^{(n-i)(m-j)} \ = &k sum_{i=1}^{n}(-1)^{i} C_{n}^{i} sum_{j=1}^{m}(-1)^{j} C_{m}^{j} cdotleft(k^{n-i} ight)^{m-j} \ = &k sum_{i=1}^{n}(-1)^{i} C_{n}^{i}left(left(k^{n-i}-1 ight)^{m}-left(k^{n-i} ight)^{m} ight) end{aligned} ]

就可以利用快速幂计算了。

#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);
    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
",(Qpow(n , k) - r - l)%kMod);
	// fclose(stdin);
	// fclose(stdout);
	return 0;
}

正解思路

由裴蜀定理得,对于一个确定的 (B) ,答案为

[sum limits_{i_{1} mid B} sum limits_{i_{2} mid B} cdots sum limits_{i_{n} mid B}left[g c dleft(i_{1}, i_{2}, cdots, i_{n}, K ight)=1] ight. ]

由此,只需要对 (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 ight)=0 ight])

即如果 (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
", (f[R] - f[L - 1] + MOD) % MOD);
}

写在最后

题目真的很有个性。

我没读懂 T2 ,另外二位没读懂 T3 ,题面是真的不错不错。

原文地址:https://www.cnblogs.com/Frather/p/14757486.html