• 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 ight floor + cnt)

    Q: 为什么

    A:

    题目要求满足

    [xy equiv 1 pmod p ]

    的无序二元组个数

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

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

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

    但是题目里要求的无序二元组 而每一个与 (p) 互质的 (x) 都对应着一个 (y)(x e 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('
    ')
    #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('
    ')
    #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 ight floor) 对左边维护后缀最大值记为 (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;}
    
    
  • 相关阅读:
    面向对象(静态,抽象,接口)--2017-04-20
    面向对象三大特性(面试经常问)--2017-04-18
    析构函数,函数重载,以及面向对象求面积的例子--2017-04-19
    密码强弱的判断(用正则表达式写)---2017-04-17
    php面向对象(一)---2017-04-17
    php数组--2017-04-16
    正则表达式 详解---2017-04-16
    JavaScript BOM 遗漏知识再整理;弹窗和记时事件;
    JavaScript HTML DOM---遗漏知识再整理(向html添加/删除元素,改变内容和css)
    bootstrap部分---网格系统;(几天没写博客了,为了潜心研究一下bootstrap)
  • 原文地址:https://www.cnblogs.com/blank-space-/p/15177766.html
Copyright © 2020-2023  润新知