zoukankan      html  css  js  c++  java
  • 8.24 校内模拟赛 题解报告

    8.24 校内模拟赛 题解报告

    T1 板子题 板子不会写 挂

    T2 数论结论题 数据锅了 挂

    T3 换根 dp 题 换根写挂 挂

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


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

    开考之后花了二十分钟把三个题的题目看了一下

    T1 给了七十分的暴力 嗯 先放着...

    很显然的 AC 自动机的模板题... 但是 AC 自动机感觉写不出来了...

    大概率会调很久... 然后跑路了

    T2 一开始看的时候有点懵 分析了一下感觉好像可做 先放着 溜去看 T3

    读完题没看出什么东西来 看了下数据 送了四十的暴力 分析了一下 要么点分治 要么就 dp

    然后跑去把 T1 的七十分拿了

    然后跑去猜了一个 T2 的结论 稍微尝试证了一下 感觉问题不大 码完自己卡了一下 好像可行的亚子 复杂度好像也没有问题 有希望能过(狂喜 虽然最后并没过

    然后跑去死磕 T3

    换根 dp 写出来调不出来系列...

    调了大半个小时 看还剩下半个小时 顺手把四十的暴力写了 最后也没调出来


    赛后看那 SB 一样的说了等于没说的题解 = =

    还是待自力更生...

    Ariel 说 T1 可以字符串哈希水过去...

    麻了 没想到用 map BS 菜死了菜死了菜死了

    但是 Ariel 哈希没取模 于是他爆了 呵呵呵呵

    麻了 T2 怎么挂了...

    前面三个点怎么过的?

    艹艹艹

    跑去看标程 嗯? 结论是对的...

    所以为什么挂了 这绝对不是我的问题 绝对是数据问题(残念 中午扒着数据看了好久 确实是数据问题

    果然 T3 只过了暴力分 调不出来的 dp 没什么指望... 后来发现 dp 的初始化和子树的 siz 锅了

    得分情况

    70 + 30 + 30 = 130

    T2 很气

    出题人数据锅了 连带着 std 跑出来的答案也锅了

    什么鬼题不开 long long 可以过 开了就 WA 啊 烦

    这个 T3 硬是 T 了也没办法


    题解

    由于此次的考试题解过于不可言述 有和没有一个样 基本什么都没给 所以一下的思路及代码均来自个人或机房中大佬的想法

    T1 monitor

    Ariel: 字符串哈希加 map 直接水过去

    然后 BS 就被卡了...

    注意模数不要开 (10^9 + 7) 改成 (10^9 + 9) 就过了


    代码

    代码比较冗杂 里面并不只有正解 还包括了所有部分分的代码

    考试的时候看一部分写一部分 想到哪写到哪了

    /*
      Source: 监听
    */
    #include<map>
    #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 base = 137;
    const int A = 1e4 + 7;
    const int B = 1e5 + 7;
    const int C = 1e6 + 7;
    const int D = 1e7 + 7;
    const int mod = 1e9 + 9;
    const int INF = 0x3f3f3f3f;
    /*----------------------------------------------------------*/
    inline void File() {
    	freopen("monitor.in", "r", stdin);
    	freopen("monitor.out", "w", stdout);
    }
    /*----------------------------------------------------------*/
    int L, n, m, H[B], h[A], p[B];
    char s1[B], s2[A][30];
    std::map <int, bool> mp;
    /*----------------------------------------------------------*/
    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 work1() {
    	for(int i = 1; i ^ L + 1; i++)
    		for(int j = 1; j ^ n + 1; j++)
    		{
    			int l = i, k = 1;
    			for(; k ^ m + 1; k++, l++)
    				if(s1[l] != s2[j][k]) break;
    			if(k == m + 1) {Print(i); return ;}
    		}
    	puts("no");
    }
    void work2() {
    	int res = 0; p[0] = 1;
    	for(int i = 1; i ^ Max(L, m) + 1; i++) p[i] = p[i - 1] * base % mod;
    	for(int i = 1; i ^ m + 1; i++) res = (res + (s2[1][i] - 96) * p[i - 1] % mod) % mod;
    	for(int i = 1; i ^ L + 1; i++)
    	{
    		H[i] = (H[i - 1] + (s1[i] - 96) * p[i - 1] % mod) % mod;
    		if(res * p[i - m] % mod == ((H[i] - H[i - m]) % mod + mod) % mod) {Print(i - m + 1); return ;}
    	}
    	puts("no");
    }
    void work3() {
    	p[0] = 1;
    	for(int i = 1; i ^ Max(L, m) + 1; i++) p[i] = p[i - 1] * base % mod;
    	for(int i = 1; i ^ n + 1; i++) for(int j = 1; j ^ m + 1; j++)
    		h[i] = (h[i] + (s2[i][j] - 96) * p[j - 1] % mod) % mod;
    	for(int i = 1; i ^ L + 1; i++)
    	{
    		H[i] = (H[i - 1] + (s1[i] - 96) * p[i - 1] % mod) % mod;
    		if(i >= m) for(int j = 1; j ^ n + 1; j++)
    			if(((H[i] - H[i - m]) % mod + mod) % mod == h[j] * p[i - m] % mod) {Print(i - m + 1); return ;}
    	}
    	puts("no");
    }
    void work4() {
    	p[0] = 1;
    	for(int i = 1; i ^ Max(L, m) + 1; i++) p[i] = p[i - 1] * base % mod;
    	for(int i = 1; i ^ n + 1; i++)
    	{
    		int res = 0;
    		for(int j = 1; j ^ m + 1; j++) res = (res + (s2[i][j] - 96) * p[j - 1] % mod) % mod;
    		mp[res] = 1;
    	}
    	for(int i = 1; i ^ L + 1; i++)
    	{
    		int res = 0;
    		for(int j = 0; j ^ m; j++) res = (res + (s1[i + j] - 96) * p[j] % mod) % mod;
    		if(mp[res]) {Print(i); return ;}
    	}
    	puts("no");
    }
    void Main() {
    //	File();
    	L = read(); n = read(); m = read();
    	for(int i = 1; i ^ n + 1; i++) scanf("%s", s2[i] + 1);
    	scanf("%s", s1 + 1);
    	if(L * n * m <= D) work1();
    	else if(n == 1) work2();
    	else if(L * n <= D) work3();
    	else work4();
    //	int x = read();
    //	if(x == 1) work1();
    //	if(x == 2) work2();
    //	if(x == 3) work3();
    }
    /*----------------------------------------------------------*/
    signed main() {Main(); return 0;}
    /*
    11 3 3
    aba
    cba
    abc
    aaabbabzabz
    */
    

    T2 lab

    对造数据的人提出谴责

    原题的数据中有给出 (T_i leq 10^9) 的范围的 但是第三, 四, 五个点的数据中依然出现了超过三十二位整型的数 而标程读入的 (T)(int) 类型 所以连带着输出答案都不对

    所以 BS 的才是对的


    这题一开始的时候是看的第一部分的暴力分 然后分析了一下 猜了个结论 算了一下复杂度...

    好像能过...(笑

    然后就写了

    最后并没有过(悲


    可以说是算是一个结论题了

    先说做法

    先对原图把树建出来 对于这个时候的询问 两点的距离是唯一的 可以直接算 然后当有加边的时候 将加入的一条边与原来的树边形成的一个环的长度记下来 维护环的长度的 (gcd) 即为 (g) 每加入一条边的时候进行维护 对于询问 如果两点之间的距离与给出的 (t) 在模 (g) 的意义下同余 则为 (yes) 否则为 (no)

    Q: 为什么

    A:

    题中给出对于每条边 正着走和反着走的代价是相反的 所以对于一条路径 来回走一遍甚至若干遍对答案是没有影响的 所以

    随着加边图中会不断构成环 不难设想可以通过一条路径到达一个环 然后可以再环上转任意多圈再回来 经过的那一条路径时不会对答案做贡献的

    所以有用的就只有那个环 而这个环是可以正着走也可以反着走的

    设两点之间的树上距离为 (dis) 环长为 (len) 的话 那么在只有一个环的时候 (u)(v) 的路径长度就是 (dis_{u, v} + k imes len)

    显然这个结论可以推广到若干个环 不妨设为 (m) 个 这样就可以得到两点之间的路径:

    [Dis_{u, v} = dis_{u, v} + sum_{i = 1}^mk_i imes len_i ]

    对于所有的 (k) 其取值都为任意整数 那么可以找到一个 (K) 使得上式为:

    [Dis_{u, v} = dis_{u, v} + K imes gcd_{i = 1}^m(len_i) ]

    BS: (gcd(a, b) = gcd(a - b, b))

    Ariel: 裴蜀定理

    BS: ...

    BS 太菜了 不知道裴蜀定理真是抱歉

    回到正题

    (g = gcd_{i = 1}^m(len_i)) 题目相当于要求我们判断 是否有:

    [dis_{u, v} + K imes g = t ]

    那个 (K) 是不确定的 但是这个式子的形式很容易启发我们将其转化为同余式 即:

    [dis_{u, v} equiv t pmod g ]

    于是就有了上面的那个做法


    一些题外话

    由于这个题的数据锅掉了 所以读入 (T) 的时候需要读入为 int 而其他位置需要用 long long 具体见代码


    T2 在评测的时候 zxsoul 以其优秀的搜索技巧 卡住评测姬数分钟

    成功耽误老吕吃饭

    我愿称你为最强

    modify(u, v, w); modify(u, v, -w);
    

    神仙建图 这你的 dfs 能出来就有鬼了 = =


    代码

    /*
      Source: 实验室
    */
    #include<cstdio>
    #include<cstring>
    #define ll 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 = 1e5 + 7;
    const int B = 5e5 + 7;
    const int C = 1e6 + 7;
    const int D = 1e7 + 7;
    const int mod = 1e9 + 7;
    const int INF = 0x3f3f3f3f;
    /*----------------------------------------------------------*/
    inline void File() {
    	freopen("lab4.in", "r", stdin);
    	freopen("lab.out", "w", stdout);
    }
    /*----------------------------------------------------------*/
    ll n, Q, dis[A], flag, g;
    struct Query {ll opt, u, v, t;} q[B];
    struct edge {ll v, w, nxt;} e[B << 1];
    ll head[A], ecnt;
    /*----------------------------------------------------------*/
    inline ll read() {
    	ll 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(ll x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
    /*----------------------------------------------------------*/
    void add_edge(int u, int v, ll w) {e[++ecnt] = (edge){v, w, head[u]}; head[u] = ecnt;}
    namespace Cut {
    	int dep[A], siz[A], top[A], fa[A], son[A], tcnt;
    	void dfs1(int u, int pre) {
    		siz[u] = 1; fa[u] = pre; dep[u] = dep[pre] + 1;
    		for(int i = head[u]; i; i = e[i].nxt)
    		{
    			ll v = e[i].v, w = e[i].w; if(v == pre) continue;
    			dis[v] = dis[u] + w; dfs1(v, u); siz[u] += siz[v];
    			if(!son[u] || siz[son[u]] < siz[v]) son[u] = v;
    		}
    	}
    	void dfs2(int u, int tp) {
    		top[u] = tp; if(!son[u]) return ; dfs2(son[u], tp);
    		for(int i = head[u]; i; i = e[i].nxt)
    		{
    			ll v = e[i].v; if(v == fa[u] || v == son[u]) continue;
    			dfs2(v, v);
    		}
    	}
    	int LCA(int x, int y) {
    		while(top[x] != top[y])
    		{
    			if(dep[top[x]] < dep[top[y]]) Swap(x, y);
    			x = fa[top[x]];
    		}
    		return dep[x] < dep[y] ? x : y;
    	}
    }
    ll gcd(ll x, ll y) {return y ? gcd(y, x % y) : x;}
    
    void Main() {
    	File();
    	n = read(); Q = read();
    	for(ll i = 1, x, y, z; i ^ n; i++) x = read(), y = read(), z = read(), add_edge(x, y, z), add_edge(y, x, -z);
    	Cut::dfs1(1, 0); Cut::dfs2(1, 1);
    	for(int i = 1; i ^ Q + 1; i++)
    	{
    		ll opt = read(), x = read(), y = read(), lca = Cut::LCA(x, y); int t = read();
    		ll Dis = (-(dis[x] - dis[lca]) + (dis[y] - dis[lca]) - t);
    		if(opt)
    		{
    			if(!g) {if(Dis) puts("no"); else puts("yes");}
    			else
    			{
    				Dis = (Dis % g + g) % g;
    				if(!Dis) puts("yes"); else puts("no");
    			}
    		}
    		else g = gcd(g, Dis);
    	}
    }
    /*----------------------------------------------------------*/
    signed main() {Main(); return 0;}
    
    

    题目已上传到洛谷 数据已经修复

    是用 BS 的代码跑的 骄傲

    将上面的那个 (int) 改成 (ll) 就好了


    T3 civilization

    换根 dp

    方程和转移都比较显然

    初始:

    (f_{u, 0/1}) 表示以 (u) 为根的子树中 到 (u) 点距离为 偶数/奇数 的点的距离和

    转移考虑 (w_{u, v}) 的奇偶性 同时需要维护到 (u) 点距离为 偶数/奇数 的点的数量

    换根:

    (f_{u, v}) 表示到 (u) 点距离为 偶数/奇数 的点的距离和

    同样转移考虑 (w_{u, v}) 的奇偶性 此时仍然需要动态的维护全树中到当前根节点距离为 偶数/奇数 的点的个数 BS 忘记这茬爆零了

    具体细节见代码 代码中 BS 是通过相乘进行分类讨论的


    代码

    /*
      Source: 文明
    */
    #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 = 1e7 + 7;
    const int mod = 1e9 + 7;
    const int INF = 0x3f3f3f3f;
    /*----------------------------------------------------------*/
    inline void File() {
    	freopen("civilization.in", "r", stdin);
    	freopen("civilization.out", "w", stdout);
    }
    /*----------------------------------------------------------*/
    int n, q, siz0[B], siz1[B], size0[B], size1[B], f[B][2], du[B];
    int dis[2021][2021], ans[2021][2];
    struct edge {int v, w, nxt;} e[B << 1];
    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, int w) {e[++ecnt] = (edge){v, w, head[u]}; head[u] = ecnt;}
    void dfs(int rt, int u, int pre) {
    	for(int i = head[u]; i; i = e[i].nxt)
    	{
    		int v = e[i].v, w = e[i].w;
    		if(v == pre) continue;
    		dis[rt][v] = dis[rt][u] + w;
    		if(dis[rt][v] & 1) ans[rt][1] += dis[rt][v];
    		else ans[rt][0] += dis[rt][v];
    		dfs(rt, v, u);
    	}
    } 
    void work1() {
    	for(int i = 1; i ^ n + 1; i++) dfs(i, i, 0);
    	for(int i = 1; i ^ q + 1; i++)
    	{
    		int x = read();
    		Print(ans[x][1]); pt; Print(ans[x][0]); pn;
    	}
    }
    void dfs1(int u, int pre) {
    	siz0[u] = 1;
    	for(int i = head[u]; i; i = e[i].nxt)
    	{
    		int v = e[i].v, w = e[i].w;
    		if(v == pre) continue;
    		dfs1(v, u);
    		siz0[u] += siz0[v] * (w & 1 ^ 1) + siz1[v] * (w & 1);
    		siz1[u] += siz0[v] * (w & 1) + siz1[v] * (w & 1 ^ 1);
    		f[u][0] += (f[v][0] + siz0[v] * w) * (w & 1 ^ 1) + (f[v][1] + siz1[v] * w) * (w & 1);
    		f[u][1] += (f[v][0] + siz0[v] * w) * (w & 1) + (f[v][1] + siz1[v] * w) * (w & 1 ^ 1);
    		
    	}
    }
    void dfs2(int u, int pre) {
    	for(int i = head[u]; i; i = e[i].nxt)
    	{
    		int v = e[i].v, w = e[i].w;
    		if(v == pre) continue;
    		size0[v] = size0[u] * (w & 1 ^ 1) + size1[u] * (w & 1);
    		size1[v] = size0[u] * (w & 1) + size1[u] * (w & 1 ^ 1);
    		f[v][0] = (f[u][1] - siz0[v] * w + (size1[u] - siz0[v]) * w) * (w & 1) + (f[u][0] - siz0[v] * w + (size0[u] - siz0[v]) * w) * (w & 1 ^ 1);
    		f[v][1] = (f[u][1] - siz1[v] * w + (size1[u] - siz1[v]) * w) * (w & 1 ^ 1) + (f[u][0] - siz1[v] * w + (size0[u] - siz1[v]) * w) * (w & 1);
    		dfs2(v, u);
    	}
    }
    void work2() {
    	dfs1(1, 0); size0[1] = siz0[1]; size1[1] = siz1[1]; dfs2(1, 0);
    	for(int i = 1; i ^ q + 1; i++)
    	{
    		int x = read();
    		Print(f[x][1]); pt; Print(f[x][0]); pn;
    	}
    }
    void Main() {
    //	File();
    	n = read(); q = read();
    	for(int i = 1, x, y, z; i ^ n; i++)
    		x = read(), y = read(), z = read(), add_edge(x, y, z), add_edge(y, x, z);
    	work2();
    }
    /*----------------------------------------------------------*/
    signed main() {Main(); return 0;}
    
    
  • 相关阅读:
    要打印
    1月21日
    弹出层layer的使用
    Python学习笔记文件操作list列表操作
    Python学习笔记控制流之布尔值
    Python学习笔记控制流之操作运算符
    Python学习笔记字符串
    Python学习笔记list_to_str列表转字符串
    DropDownList 下拉无限极分类代码
    Jquery 基础教程测试
  • 原文地址:https://www.cnblogs.com/blank-space-/p/15181628.html
Copyright © 2011-2022 走看看