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:
题目要求满足
的无序二元组个数
看一下上面那个东西 大概就能看出来了 就是个逆元
这里的 \(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;}