// // // // // // // // // // // // // //

8.23 校内模拟赛 题解报告

8.23 校内模拟赛 题解报告

花式白给

T1 SB 数论题 苟比结论题 不过我过了(笑

T2 鬼畜构造题 完全木有想到 暴力骗分苟命

T3 神仙二分题 压根不会 \(O(n^2)\) 苟命

大概这场考试就是这样...


关于考试过程以及一些题外话

这次考试是从 T3 开的... 于是就把人玩没了

看题 嗯 10 分 \(O(n^2)\) 送的

看题 嗯 好像就会 10 分的亚子

看题 嗯 确实就会 10 分的亚子

划拉了半张打草纸之后...

看题 嗯...

然后就把那十分写了滚蛋了

回去老老实实看 T1

看题 嗯 这啥啊

看题 嗯 好像能做

看题 嗯 好像确实能做

在纸上划拉了一会...

看题 嗯 SB 题

虽然 BS 这个题的代码写的很 SB 但是确实是把大样例过掉了

去骗 T2

先拿了那十分的暴力 然后写掉二十分的构造 然后就没有然后了...

看题 好像不太可做的亚子

于是死磕 T3 嗝屁了


结束以后 由于老吕没来

SB 的 BS 对着电脑捣鼓了半个小时 依旧没把评测的东西弄好 然后评测一直拖到了下午...


得分情况

100 + 30 + 10 = 140

虽然 T1 的代码很诡异 但是还是过掉了

T2 和 T3 的暴力都没挂骗到了四十 大概也就是 T1 的水平了...


题解

T1 Count

要结论的话很简单

枚举 \([1, p)\) 范围内的所有数 \(i\) 记录 \(i^2 \equiv 1 \pmod p\) 的数的数量

答案: \(ans = \left \lfloor \frac {\varphi(p) - cnt}2 \right \rfloor + cnt\)

Q: 为什么

A:

题目要求满足

\[xy \equiv 1 \pmod p \]

的无序二元组个数

看一下上面那个东西 大概就能看出来了 就是个逆元

这里的 \(p\) 不一定是质数 所以逆元不一定存在 当 \(a \bot p\)\(a\) 存在模 \(p\) 意义下的逆元且逆元唯一

所以这个题就相当于求与 \(p\) 互质的数的个数 显然就是 \(\varphi(p)\)

但是题目里要求的无序二元组 而每一个与 \(p\) 互质的 \(x\) 都对应着一个 \(y\)\(x \ne y\) 的时候就会算重 所以这就是上面统计平方模 \(p\)\(1\) 的数的个数的原因了


求那个 \(\varphi\) 的时候 以为只能用素数去筛 于是就先把素数筛出来再用素数筛 \(\varphi\) 忘记可以直接 \(\sqrt n\) 求了...

于是就写了一个线筛筛素数不晒 \(\varphi\) 函数 筛完素数 拿着筛出来的素数又把 \(\varphi\) 求了一遍的诡异代码


代码

/*
  Source: count
*/
#include<cstdio>
#include<cstring>
#define int long long
#define pt putchar(' ')
#define pn putchar('\n')
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 5e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*----------------------------------------------------------*/
inline void File() {
	freopen("count.in", "r", stdin);
	freopen("count.out", "w", stdout);
}
/*----------------------------------------------------------*/
int p, prime[C << 2], cnt, kcnt, Phi;
bool vis[D];
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
int power(int x, int y) {int res = 1; for(; y; x = x * x % p, y >>= 1) if(y & 1) res = res * x % p; return res;}
void exgcd(int a, int b, int &d, int &x, int &y)
{
	if(b) exgcd(b, a % b, d, y, x), y -= x * (a / b);
	else d = a, x = 1, y = 0;
}
void pre() {
	for(int i = 2; i ^ p + 1; i++)
	{
		if(!vis[i]) prime[++cnt] = i;
		for(int j = 1; j ^ cnt + 1 && i * prime[j] <= p; j++)
		{
			vis[i * prime[j]] = 1;
			if(!(i % prime[j])) break;
		}
	}
}
void Main() {
	File();
	Phi = p = read(); pre();
	for(int i = 1; i ^ p; i++) if(i * i % p == 1) kcnt++;
	for(int i = 1; i ^ cnt + 1 && prime[i] * prime[i] <= p; i++) if(!(p % prime[i]))
	{
		Phi = Phi / prime[i] * (prime[i] - 1);
		while(!(p % prime[i])) p /= prime[i];
	}
	if(p > 1) Phi = Phi / p * (p - 1);
	Print((Phi - kcnt >> 1) + kcnt);
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}


T2 Color

暴力挺好写的 直接给结论吧

对于 \(x\) 坐标相同的点 将这些点两两配对 如果剩下点则不管 然后每对点之间连边

对于 \(y\) 坐标相同的点 将这些点两两配对 如果剩下点则不管 然后每对点之间连边

对建成的图进行染色 能染出来就表示可行 方案也就有了

Q: 为什么

A: 不清楚 意会一下大概是可行的

Q: 能过吗

A: 能过 而且跑挺快的

Q: 有不合法的情况吗

A: 不知道 但是没有判确实过了


代码

/*
  Source: CF547D Mike and Fish
*/
#include<cstdio>
#include<cstring>
#define pt putchar(' ')
#define pn putchar('\n')
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int A = 1e4 + 7;
const int B = 2e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*----------------------------------------------------------*/
inline void File() {
	freopen(".in", "r", stdin);
	freopen(".out", "w", stdout);
}
/*----------------------------------------------------------*/
int n, col[B], lc[B], lr[B];
struct edge {int v, nxt;} e[B << 2];
int head[B], ecnt;
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
void add_edge(int u, int v) {e[++ecnt] = (edge){v, head[u]}; head[u] = ecnt;}
void dfs(int u, int c) {
	col[u] = c;
	for(int i = head[u]; i; i = e[i].nxt) if(!~col[e[i].v]) dfs(e[i].v, c ^ 1);
}
void Main() {
	n = read();
	for(int i = 1; i ^ n + 1; i++)
	{
		int x = read(), y = read(); col[i] = -1;
		if(lr[x]) add_edge(lr[x], i), add_edge(i, lr[x]), lr[x] = 0; else lr[x] = i;
		if(lc[y]) add_edge(lc[y], i), add_edge(i, lc[y]), lc[y] = 0; else lc[y] = i;
	}
	for(int i = 1; i ^ n + 1; i++) if(!~col[i]) dfs(i, 0);
	for(int i = 1; i ^ n + 1; i++) putchar(col[i] ? 'r' : 'b');
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}


据说正解是欧拉回路什么的...

然鹅并不会


T3 Sequence

神仙分治题

由于过于麻烦 这里直接说做法了


\(mid = \left \lfloor \frac {l + r}2 \right \rfloor\) 对左边维护后缀最大值记为 \(max\) 后缀最小值记为 \(min\) 右边维护前缀最大值\(max\) 前缀最小值\(min\) 前缀最大值的前缀和\(summax\) 前缀最小值的前缀和\(summin\) 前缀最大值与最小值的乘积和\(summul\)

枚举左侧每个位置 对每个 \(i\) 找到第一个位置 \(tmp1\) 使 \(min_{tmp1} \leq min_i\)\(min_{tmp1 - 1} > min_i\) 同时找到第一个位置 \(tmp2\) 使 \(max_{tmp2} \geq max_i\)\(max_{tmp2 - 1} < max_i\) 将右边的区间分为三段 利用上面维护的各种东西就可以 \(O(1)\) 的算出右端点在右边区间内的价值和

递归处理左右两部分


各种边界细节问题极其鬼畜


代码

/*
  Source: T196991 Sequence
*/
#include<cstdio>
#define int long long
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
/*----------------------------------------------------------*/
const int B = 5e5 + 7;
const int mod = 998244353;
/*----------------------------------------------------------*/
int n, a[B], min[B], max[B], summin[B], summax[B], summul[B], ans;
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
void solve(int L, int R) {
	if(L == R) {ans = (ans + a[L] * a[L] % mod) % mod; return ;}
	int Mid = L + R >> 1;
	min[Mid] = max[Mid] = a[Mid]; min[Mid + 1] = max[Mid + 1] = a[Mid + 1];
	summin[Mid] = summax[Mid] = summul[Mid] = 0;
	summin[Mid + 1] = summax[Mid + 1] = a[Mid + 1]; summul[Mid + 1] = a[Mid + 1] * a[Mid + 1] % mod;
	for(int i = Mid - 1; i >= L; i--)
		min[i] = Min(min[i + 1], a[i]), max[i] = Max(max[i + 1], a[i]);
	for(int i = Mid + 2; i <= R; i++)
		min[i] = Min(min[i - 1], a[i]), summin[i] = (summin[i - 1] + min[i]) % mod,
		max[i] = Max(max[i - 1], a[i]), summax[i] = (summax[i - 1] + max[i]) % mod,
		summul[i] = (summul[i - 1] + min[i] * max[i] % mod) % mod;
	for(int i = Mid, tmp1 = Mid + 1, tmp2 = Mid + 1; i >= L; i--)
	{
		while(tmp1 <= R && min[tmp1] > min[i]) tmp1++;
		while(tmp2 <= R && max[tmp2] < max[i]) tmp2++;
		int x = Min(tmp1, tmp2), y = Max(tmp1, tmp2);
		ans = (ans + (x - 1 - Mid) * min[i] % mod * max[i] % mod) % mod;
		if(tmp1 < tmp2) ans = (ans + (summin[tmp2 - 1] - summin[tmp1 - 1]) % mod * max[i] % mod) % mod;
		if(tmp1 > tmp2) ans = (ans + (summax[tmp1 - 1] - summax[tmp2 - 1]) % mod * min[i] % mod) % mod;
		ans = ((ans + summul[R] - summul[y - 1]) % mod + mod) % mod;
	}
	solve(L, Mid); solve(Mid + 1, R);
}
void Main() {
	n = read();
	for(int i = 1; i ^ n + 1; i++) a[i] = read();
	solve(1, n);
	Print(ans);
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}

posted @ 2021-08-23 21:05  Blank_space  阅读(45)  评论(8编辑  收藏  举报
// // // // // // //