• JZOJ 2022.07.06【提高组A】模拟


    历程

    被暴打了
    原因是钻进了 \(T4\) 的坑中。。。
    先看完题,发现 \(T4\) 比较有意思,\(T2\) 没有想法
    \(T3\) 挺容易,做法似乎很好想
    \(T1\) 送分,十几分钟搞定
    然后开 \(T4\),推了一下,明白题目需要我们干的事情
    开码,发现修改与标记有些烦,然后标记打错了,一直调到 \(11\)
    疯了!
    还有 \(\text{45 min}\),只要忍痛弃了
    看看 \(T3\),很好做,\(5 min\) 打完
    有充满自信,放弃 \(T2\) 部分分,继续调 \(T4\)
    但始终没有意识到一处标记的问题
    导致比赛结束被暴打(更重要的是,暴力每题普遍 \(80,90pts\) !!神仙数据。。。)

    \(\text{T1 1782. Travel}\)

    这不 \(Floyd\) 预处理后分层最短路 \(spfa\) 啊!
    正好复习最短路模板

    $\text{Code}$

    \(\text{Code}\)

    #include <cstdio> 
    #include <queue>
    #include <cstring>
    #include <iostream>
    #define RE register
    using namespace std;
    
    const int N = 105, INF = 0x3f3f3f3f;
    int n, m, q, G[N][N], vis[N][N], bz[N][15], f[N][15];
    struct node{int x, k;};
    queue<node> Q;
    
    void SPFA()
    {
    	memset(f, INF, sizeof f), Q.push(node{1, 0}), f[1][0] = 0, bz[1][0] = 1;
    	while (!Q.empty())
    	{
    		node z = Q.front(); Q.pop();
    		for(RE int i = 1; i <= n; i++)
    		if (vis[z.x][i] && i ^ z.x)
    		{
    			int j = i, k = z.k, w = G[z.x][i];
    			if (!vis[i][z.x]) w *= 2, ++k;
    			if (k <= q && f[j][k] > f[z.x][z.k] + w)
    			{
    				f[j][k] = f[z.x][z.k] + w;
    				if (!bz[j][k]) Q.push(node{j, k}), bz[j][k] = 1;
    			}
    		}
    		bz[z.x][z.k] = 0;
    	}
    }
    
    int main()
    {
    	scanf("%d%d%d", &n, &m, &q), memset(G, INF, sizeof G);
    	for(RE int i = 1, x, y, z; i <= m; i++)
    		scanf("%d%d%d", &x, &y, &z), G[x][y] = min(G[x][y], z), vis[x][y] = 1;
    	for(RE int k = 1; k <= n; k++)
    		for(RE int i = 1; i <= n; i++)
    		if (i ^ k)
    			for(RE int j = 1; j <= n; j++)
    			if (j ^ i && j ^ k)
    				vis[i][j] = (vis[i][j] | (vis[i][k] && vis[k][j]));
    	SPFA();
    	int ans = INF;
    	for(RE int i = 1; i <= q; i++) ans = min(ans, f[n][i]);
    	if (ans == INF) ans = -1;
    	printf("%d\n", ans);
    }
    

    \(\text{T2 3337. wyl8899的TLE}\)

    思考答案的产生
    \(A\) 的首位, \(B\) 的某个位置开始,求 \(LCP\)
    然后魔法地同时跳过一格(计入贡献),再求 \(LCP\)
    答案取最大值即可
    也就是说可以枚举 \(B\) 的开始位置,二分加哈希求 \(LCP\) 即可
    思维真不高。。。
    也算复习字符串基操吧

    $\text{Code}$

    \(\text{Code}\)

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #define RE register
    #define IN inline
    using namespace std;
    typedef long long LL;
    
    const int N = 5e4 + 5, P1 = 1e9 + 7, P2 = 1e9 + 9, B = 29;
    char s1[N], s2[N];
    LL b1[N], b2[N];
    
    struct Hash{
    	int n; LL p1[N], p2[N];
    	IN void init(char *s)
    	{
    		n = strlen(s + 1);
    		for(RE int i = 1; i <= n; i++)
    			p1[i] = (p1[i - 1] * B + s[i] - 'a') % P1, p2[i] = (p2[i - 1] * B + s[i] - 'a') % P2;
    	}
    	IN int get1(int l, int r){return (p1[r] - p1[l - 1] * b1[r - l + 1] % P1 + P1) % P1;}
    	IN int get2(int l, int r){return (p2[r] - p2[l - 1] * b2[r - l + 1] % P2 + P2) % P2;}
    }h1, h2;
    
    IN int lcp(int l1, int l2)
    {
    	int l = 0, r = min(h1.n - l1 + 1, h2.n - l2 + 1), mid = l + r >> 1, res = 0;
    	for(; l <= r; mid = l + r >> 1)
    		if ((h1.get1(l1, l1 + mid - 1) == h2.get1(l2, l2 + mid - 1)) 
    			&& (h1.get2(l1, l1 + mid - 1) == h2.get2(l2, l2 + mid - 1))) res = mid, l = mid + 1;
    		else r = mid - 1;
    	return res;
    }
    
    int main()
    {
    	b1[0] = b2[0] = 1;
    	for(RE int i = 1; i <= N - 3; i++) b1[i] = b1[i - 1] * B % P1, b2[i] = b2[i - 1] * B % P2;
    	scanf("%s%s", s1 + 1, s2 + 1), h1.init(s1), h2.init(s2);
    	int ans = 0;
    	for(RE int i = 1, len; i <= h2.n; i++)
    		ans = max(ans, (len = lcp(1, i)) + lcp(len + 2, i + len + 1) + (len < h1.n && i + len - 1 < h2.n));
    	printf("%d\n", ans);
    }
    

    \(\text{T3 3338. 法法塔的奖励}\)

    考虑一个点的答案,显然由子树符合条件的 \(dp\) 值转来
    发现是维护子树的问题,所以树上启发式合并加树状数组做到 \(O(n\log^2 n)\) 或者线段树合并做到 \(O(n\log n)\)
    当然是打前者啦,跟打暴力一样一样的

    $\text{Code}$

    \(\text{Code}\)

    #include <cstdio>
    #include <iostream>
    #define RE register
    #define IN inline
    using namespace std;
    
    const int N = 1e5 + 5;
    int n, fa[N], a[N], h[N], tot, c[N];
    int dfc, siz[N], dfn[N], rev[N], son[N], ans[N];
    struct edge{int to, nxt;}e[N];
    IN void add(int x, int y){e[++tot] = edge{y, h[x]}, h[x] = tot;}
    
    void dfs1(int x)
    {
    	siz[x] = 1, dfn[x] = ++dfc, rev[dfc] = x;
    	for(RE int i = h[x]; i; i = e[i].nxt)
    	{
    		int v = e[i].to;
    		dfs1(v), siz[x] += siz[v];
    		if (siz[v] > siz[son[x]]) son[x] = v;
    	}
    }
    
    IN int lowbit(int x){return x & (-x);}
    IN void update(int x, int v){for(; x <= n; x += lowbit(x)) c[x] = max(c[x], v);}
    IN void clear(int x){for(; x <= n; x += lowbit(x)) c[x] = 0;}
    IN int query(int x)
    {
    	int res = 0;
    	for(; x; x -= lowbit(x)) res = max(res, c[x]);
    	return res;
    }
    
    void dfs2(int x, int kp)
    {
    	for(RE int i = h[x]; i; i = e[i].nxt)
    	{
    		int v = e[i].to;
    		if (v == son[x]) continue;
    		dfs2(v, 0);
    	}
    	if (son[x]) dfs2(son[x], 1);
    	for(RE int i = h[x]; i; i = e[i].nxt)
    	{
    		int v = e[i].to;
    		if (v == son[x]){update(a[son[x]], ans[son[x]]); continue;}
    		for(RE int j = dfn[v]; j <= dfn[v] + siz[v] - 1; j++) update(a[rev[j]], ans[rev[j]]);
    	}
    	ans[x] = query(a[x]) + 1;
    	if (!kp) for(RE int i = dfn[x] + 1; i <= dfn[x] + siz[x] - 1; i++) clear(a[rev[i]]);
    }
    
    int main()
    {
    	scanf("%d", &n); int x; scanf("%d", &x);
    	for(RE int i = 2; i <= n; i++) scanf("%d", &fa[i]), add(fa[i], i);
    	for(RE int i = 1; i <= n; i++) scanf("%d", &a[i]);
    	dfs1(1), dfs2(1, 1);
    	for(RE int i = 1; i <= n; i++) printf("%d ", ans[i]);
    }
    

    \(\text{T4 3339. wyl8899和法法塔的游戏}\)

    很容易想到 \(nim\) 游戏的结论:异或和为零为先手必败态
    那么先求异或和,需要第一步取完后留给对手必败态,即 \((a_r-x) \oplus \text{others} = 0\)
    \(x=a_r-\text{others}\) 最大化,可持久化 \(Trie\) 即可
    但是需要支持修改操作,所以考虑分块加 \(Trie\)
    注意打标记时的细节

    $\text{Code}$

    \(\text{Code}\)

    #include <cstdio>
    #include <cmath>
    #include <iostream>
    #define IN inline
    #define RE register
    using namespace std;
    
    const int N = 1e5 + 5, B = 319;
    int n, m;
    
    struct DS{
    	int pos[N], block, a[N], tag[B], tr[N * 20][2], size, sum[N * 20], rt[B];
    	IN void init()
    	{
    		block = sqrt(n);
    		for(RE int i = 1; i <= n; i++) pos[i] = (i - 1) / block + 1;
    	}
    	IN void update(int p, int x, int v)
    	{
    		if (!rt[p]) rt[p] = ++size;
    		int u = rt[p];
    		for(RE int i = 10; i >= 0; i--)
    		{
    			int ch = (x >> i) & 1;
    			if (!tr[u][ch]) tr[u][ch] = ++size;
    			sum[u = tr[u][ch]] += v;
    		}
    	}
    	IN int query(int p, int v)
    	{
    		int u = rt[p], res = 0;
    		for(RE int i = 10; i >= 0; i--)
    		{
    			int ch = (v >> i) & 1;
    			if (sum[tr[u][ch]]) u = tr[u][ch];
    			else if (sum[tr[u][ch ^ 1]]) u = tr[u][ch ^ 1], res |= (1 << i);
    		}
    		return res;
    	}
    	IN int get(int x){return a[x] ^ tag[pos[x]] ^ a[x + 1] ^ tag[pos[x + 1]];}
    	IN void Modify(int r, int v)
    	{
    		int t = get(r), w = a[r] ^ (t - v) ^ tag[pos[r]] ^ a[r + 1] ^ tag[pos[r + 1]];
    		for(RE int i = r; i >= (pos[r] - 1) * block + 1; i--)
    			update(pos[r], a[i], -1), update(pos[r], a[i] ^= w, 1);
    		for(RE int i = 1; i < pos[r]; i++) tag[i] ^= w;
    	}
    	IN int Query(int l, int r, int v)
    	{
    		int res = N;
    		for(RE int i = l; i <= min(pos[l] * block, r); i++) res = min(res, a[i] ^ tag[pos[l]] ^ v);
    		if (pos[l] ^ pos[r])
    			for(RE int i = (pos[r] - 1) * block + 1; i <= r; i++) res = min(res, a[i] ^ tag[pos[r]] ^ v);
    		for(RE int i = pos[l] + 1; i < pos[r]; i++) res = min(res, query(i, v ^ tag[i]));
    		return res;
    	}
    }T;
    
    int main()
    {
    	scanf("%d", &n);
    	for(RE int i = 1; i <= n; i++) scanf("%d", &T.a[i]);
    	T.init();
    	for(RE int i = n; i; i--) T.a[i] ^= T.a[i + 1], T.update(T.pos[i], T.a[i], 1);
    	scanf("%d", &m);
    	for(RE int i = 1, ed, L, R; i <= m; i++)
    	{
    		scanf("%d%d%d", &ed, &L, &R);
    		int ans = T.get(ed) - T.Query(L, R, T.a[ed] ^ T.tag[T.pos[ed]]);
    		if (ans <= 0) ans = -1;
    		if (ans != -1) T.Modify(ed, ans);
    		printf("%d\n", ans);
    	}
    }
    

    可见这套题挺简单的,但是考场要分配好时间,避免陷入一处坑很久导致意外或者“正常”死亡

  • 相关阅读:
    Swing中如何比较好的判断鼠标左键双击
    学习rsyslog
    学习rsync
    在线手册
    Linux开源镜像站大全
    Linux命令
    Android使用sqlite数据库的使用
    Android学习笔记-listview实现方式之BaseAdapter
    Android学习笔记-保存数据的实现方法2-SharedPreferences
    Android学习笔记-获取手机内存,SD卡存储空间。
  • 原文地址:https://www.cnblogs.com/leiyuanze/p/16451512.html
Copyright © 2020-2023  润新知