2021.1.21 模拟赛题解

T1. star

给出 \(k,m\),求方程 \(k^x=1\pmod{m}\) 的 \(x\) 的最小正整数解。

若无解,输出 No Solution

多测,\(1 \leq T \leq 10^3,2 \leq m,k\leq 10^9\)。

sol

无解:\(\gcd(k,m) \ne 1\)。

套个 BSGS \(\mathcal O(\sqrt{n})\) 求即可。

#include <bits/stdc++.h>

using namespace std;

#define int long long

#define pii pair<int, int>

#define mp(a, b) make_pair(a, b)

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 * 10 + c - '0';
		c = getchar();
	}
	return x * f;
}

void write(int x)
{
	if(x < 0)
	{
		putchar('-');
		x = -x;
	}
	if(x > 9) write(x / 10);
	putchar(x % 10 + '0');
}

template<typename T>
T exgcd(T a, T b, T &x, T &y)
{
	if(!b) return x = 1, y = 0, a;
	int d = exgcd(b, a % b, y, x);
	return y -= a / b * x, d;
}

int gcd(int a, int b)
{
	if(!b) return a;
	return gcd(b, a % b);
}


inline int BSGS(int a, int b, int m)
{
	static unordered_map<int, int> buk;
	buk.clear();
	int S = sqrt(m - 1) + 1;
	int A = 1, f = -1;
	for(int i = 0; i < S; ++i)
	{
		buk[b * A % m] = i;
		A = A * a % m;
	}
	int C = 1;
	for(int i = 1; i <= S; ++i)
	{
		C = C * A % m;
		auto it = buk.find(C);
		if(it != buk.end())
			return i * S - it->second;
	}
	return -1;
}


int a, b, m;

signed main()
{
	int t = read();
	while(t--)
	{
		m = read(), a = read();
		a %= m;
		if(__gcd(a, m) != 1 || a == 0)
		{
			puts("No Solution");
			continue;
		}
		int ans = BSGS(a, 1, m);
		write(ans), putchar('\n');
	}
	return 0;
}

T2. 信号

给出 \(S,p,m,q,n\),求方程 \(p+mx \equiv q+nx \pmod{S}\) 的最小 \(x\) 的正整数解。

\(1 \leq p,q,m,n,S < 2^{31}\)。

sol

套个 exgcd 即可。

#include <bits/stdc++.h>

using namespace std;

#define int long long

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 * 10 + c - '0';
		c = getchar();
	}
	return x * f;
}

void write(int x)
{
	if(x < 0)
	{
		putchar('-');
		x = -x;
	}
	if(x > 9) write(x / 10);
	putchar(x % 10 + '0');
}

int s, p, m, q, n, x, y;

int gcd(int a, int b)
{
	if (b == 0)
		return a;
	return gcd(b, a % b);
}

int exgcd(int a, int b, int &x, int &y)
{
	if (b == 0)
	{
		x = 1, y = 0;
		return a;
	}
	int g = exgcd(b, a % b, x, y);
	int t = x;
	x = y;
	y = t - a / b * y;
	return g;
}

signed main()
{
	s = read(), p = read(), m = read(), q = read(), n = read();
	int a = ((n - m) % s + s) % s, c = ((p - q) % s + s) % s;
	if (c % gcd(a, s) != 0)
	{
		puts("Cannot Get The Secret!");
		return 0;
	}
	int g = exgcd(a, s, x, y);
	x *= (c / gcd(a, s));
	int kkk = s / g;
	x = (x % kkk + kkk) % kkk;
	if (x == 0)
		x += kkk;
	write(x);
	return 0;
}

T3. Modulus

给你 \(n,m\),表示有 \(m\) 个询问,每组询问形如 l,r,表示求 \(\forall l \leq j \leq r,\max(n \bmod j)\)。

\(1 \leq n \leq 10^{10},1 \leq m \leq 10^5\)。

sol

显然的数论分块。

但显然不能边数论分块边回答询问。

考虑将数论分块预处理好。

根据 \(n \bmod j=n-\lfloor\frac{n}{j}\rfloor \cdot j\)。

可转换为求 \(\lfloor\frac{n}{j}\rfloor \cdot j\) 的最小值。

根据 \(\lfloor\frac{n}{j}\rfloor\) 的值相同的一个块内,最小值显然处于左端点。

所以上述做法正确性显然。

细节:

\(l\) 如果不处于它块的左端点,那么它这个块就需要特判一下。

#include <bits/stdc++.h>

using namespace std;

#define int long long

inline 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 * 10 + c - '0';
		c = getchar();
	}
	return x * f;
}

inline void write(int x)
{
	if(x < 0)
	{
		putchar('-');
		x = -x;
	}
	if(x > 9) write(x / 10);
	putchar(x % 10 + '0');
}

int n, m, sq;

const int _ = 4e5 + 7;

int tot, p[_], q[_];

int a[_][22];

int lg[_];

void update()
{
    for (int j = 1; (1 << j) <= tot; j++)
    {
        for (int i = 1; i + (1 << j) - 1 <= tot; i++)
        {
            a[i][j] = min(a[i][j - 1], a[i + (1 << (j - 1))][j - 1]);
        }
    }
}

int query(int l, int r)
{
    if (l > r)
        return 0;
    int t = lg[r - l + 1] - 1;
    return min(a[l][t], a[r - (1 << t) + 1][t]);
}

int erfenl(int x)
{
	int l = 1, r = tot, mid;
	while(l <= r)
	{
		mid = (l + r) >> 1;
//		cout << mid << "\n";
		if(x >= p[mid] && x <= q[mid])
		{
			if(x > p[mid]) return mid + 1;
			return mid;
		}
		else if(x < p[mid])
		{
			r = mid - 1;
		}
		else if(x > q[mid])
		{
			l = mid + 1;
		}
	}
}

int erfenr(int x)
{
	int l = 1, r = tot, mid, ans = 0;
	while(l <= r)
	{
		mid = (l + r) >> 1;
		if(x >= p[mid] && x <= q[mid])
		{
			return mid;
		}
		else if(x < p[mid])
		{
			r = mid - 1;
		}
		else
		{
			l = mid + 1;
		}
	}
}

signed main()
{
//	freopen("ex.in", "r", stdin);
//	freopen("2.out", "w", stdout);
	for(int i = 0; i < _; ++i)
		for(int j = 0; j < 21; ++j) a[i][j] = 1e10 + 7;
	n = read(), m = read();
	sq = sqrt(n);
	for(int i = 1, j; i <= n; i = j + 1)
	{
		j = n / (n / i);
		p[++tot] = i, q[tot] = j;
		a[tot][0] = (n / i) * p[tot];
	}
	for (int i = 1; i <= tot; ++i)
        lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);
	update();
	while(m--)
	{
		int l = read(), r = read();
		if(l > r) swap(l, r);
		int L = erfenl(l), R = erfenr(r);
		if(L > R)
		{
			write(n - (n / l) * l);
			putchar('\n');
			continue;
		}
		write(n - query(L, R));
		putchar('\n');
	}
	return 0;
}

T4. 教主的魔法

区间加,区间查询 \(\ge c\) 的数的个数。

sol

分块。

区间加用 lazytag 维护。

查询二分预处理即可。

#include <bits/stdc++.h>

using namespace std;

inline 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 * 10 + c - '0';
		c = getchar();
	}
	return x * f;
}

inline void write(int x)
{
	if(x < 0)
	{
		putchar('-');
		x = -x;
	}
	if(x > 9)
		write(x / 10);
	putchar(x % 10 + '0');
}

const int _ = 1e6 + 7;

int n, q;

int a[_], l[_], r[_], d[_], bel[_], lz[_];

int blo, tot;

void cg(int x)
{
	for(int i = l[bel[x]]; i <= r[bel[x]]; ++i) d[i] = a[i];
	sort(d + l[bel[x]], d + r[bel[x]] + 1);
}

void change(int x, int y, int k)
{
	if(bel[x] == bel[y])
	{
		for(int i = x; i <= y; ++i) a[i] += k;
		cg(x);
	}
	else
	{
		for(int i = x; i <= r[bel[x]]; ++i) a[i] += k;
		cg(x);
		for(int i = l[bel[y]]; i <= y; ++i) a[i] += k;
		cg(y);
		for(int i = bel[x] + 1; i <= bel[y] - 1; ++i) lz[i] += k;
	}
}

int query(int x, int y, int k)
{
	int ans = 0;
	if(bel[x] == bel[y])
	{
		for(int i = x; i <= y; ++i)
			if(lz[bel[x]] + a[i] >= k) ans++;
		return ans;
	}
	else
	{
		for(int i = x; i <= r[bel[x]]; ++i)
			if(lz[bel[x]] + a[i] >= k) ans++;
		for(int i = l[bel[y]]; i <= y; ++i)
			if(lz[bel[y]] + a[i] >= k) ans++;
		for(int i = bel[x] + 1; i <= bel[y] - 1; ++i)
			ans += r[i] - (lower_bound(d + l[i], d + r[i] + 1, k - lz[i]) - d) + 1;
		return ans;
	}
}

signed main()
{
	n = read(), q = read();
	for(int i = 1; i <= n; ++i) a[i] = read();
	blo = sqrt(n), tot = n / blo;
	if(n % blo) tot++;
	for(int i = 1; i <= n; ++i)
		bel[i] = (i - 1) / blo + 1, d[i] = a[i];
	for(int i = 1; i <= tot; ++i)
		l[i] = (i - 1) * blo + 1, r[i] = i * blo;
	r[tot] = n;
	for(int i = 1; i <= tot; ++i)
		sort(d + l[i], d + r[i] + 1);
	while(q--)
	{
		char s[10];
		scanf("%s", s);
		int x = read(), y = read(), c = read();
		if(x > y) swap(x, y);
		if(s[0] == 'M')
		{
			change(x, y, c);
		}
		else
		{
			write(query(x, y, c));
			putchar('\n');
		}
	}
	return 0;
}
上一篇:centos共享目录


下一篇:逆向工程核心原理之第20章之正面分析