zoukankan      html  css  js  c++  java
  • [Cnoi2020] 题解

    整体感受

    先来说一下我的整体感受。

    首先这个A的难度还是可以的,毕竟A题本来的定位就是送分,而且也不是纯模拟枚举就可以过的无脑题。不过提高组以上的选手大概都可以1min切?

    B的话虽然水但也不是那么模板吧,反正B本来也是送分的。

    这个C、D都是概率期望对数学不好的一点都不友好,而且被喷板子。

    总体来说还是可以的吧,考察也还算全面。因为中间被拉去搞whk所以只切了AB和C的40分/kk。

    Solution

    [Cnoi2020]子弦

    出现次数最多的非空子串所包含的每个单独的字符也都跟着出现了,所以长度为1的串出现次数只多不少,所以只考虑长度为1的串就行了。

    然而其实这题还是后缀自动机的板子。

    namespace Solve{
    	static int cnt[26], ans;
    	static char s[10000010];
    	void MAIN() {
    		scanf("%s", s + 1);
    		int len = strlen(s + 1);
    		for (int i = 1; i <= len; i++) cnt[s[i] - 'a']++;
    		for (int i = 0; i < 26; i++) ans = max(ans, cnt[i]);
    		StandardIO :: print(ans);
    	}
    }
    

    [Cnoi2020]雷雨

    一定是先分别到一个点然后剩下的路一起走,所以枚举那个点,然后算3个最短路就好了。(也被喷最短路板子)

    namespace Solve{
    	const int MAXN = 1010;
    	const long long inf = 0x3f3f3f3f3f3f3f3f;
    	struct node{
    		int x, y;
    		long long dis;
    		friend bool operator < (node pp, node qq) {
    			return pp.dis > qq.dis;
    		}
    	};
    	priority_queue<node> q;
    	static int n, m, a, b, c;
    	static int dx[4] = {0, 0, 1, -1};
    	static int dy[4] = {1, -1, 0, 0};
    	static long long dis[3][MAXN][MAXN], mp[MAXN][MAXN], ans;
    	bool vis[MAXN][MAXN];
    	void calc(int id, int sx, int sy) {
    		memset(vis, false, sizeof(vis));
    		dis[id][sx][sy] = mp[sx][sy];
    		while (!q.empty()) q.pop(); 
    		q.push(node{sx, sy, dis[id][sx][sy]});
    		while (!q.empty()) {
    			node cur = q.top();
    			q.pop();
    			if (vis[cur.x][cur.y]) continue;
    			vis[cur.x][cur.y] = true;
    			for (int i = 0; i < 4; i++) {
    				node nxt = node{cur.x + dx[i], cur.y + dy[i], cur.dis + mp[cur.x + dx[i]][cur.y + dy[i]]};
    				if (dis[id][nxt.x][nxt.y] > nxt.dis) {
    					dis[id][nxt.x][nxt.y] = nxt.dis;
    					q.push(nxt);
    				}
    			}
    		}
    	}
    	void MAIN() {
    		StandardIO :: read(n); StandardIO :: read(m);
    		StandardIO :: read(a); StandardIO :: read(b); StandardIO :: read(c);
    		for (int i = n; i >= 1; i--) {
    			for (int j = 1; j <= m; j++) {
    				StandardIO :: read(mp[i][j]);
    				dis[0][i][j] = dis[1][i][j] = dis[2][i][j] = inf;
    			}
    		}
    		calc(0, n, a);
    		calc(1, 1, b);
    		calc(2, 1, c);
    		ans = inf;
    		for (int i = 1; i <= n; i++) {
    			for (int j = 1; j <= m; j++) {
    				ans = min(ans, dis[0][i][j] + dis[1][i][j] + dis[2][i][j] - 2 * mp[i][j]);
    			}
    		}
    		StandardIO :: print(ans);
    	}
    }
    

    [Cnoi2020]梦原

    考虑一个点 (v) 接在另一个点 (u) 的后面,如果 (a_u > a_v) 则在消 (u) 的过程中就可以把 (v) 消掉了,所以只考虑 (a_u<a_v)

    这种情况的贡献是 (a_v-a_u),类似的题:积木大赛。再乘上 (v) 连边的概率就好了。

    实现考虑树状数组。

    namespace Solve{
    	const long long mod = 998244353;
    	const int MAXN = 1000010;
    	static int n, nn, k, h[MAXN], g[MAXN];
    	static long long ans;
    	static long long sum[MAXN], num[MAXN];
    	int lowbit(int x) { return x & -x; }
    	void Sub(int x, long long v) { while (x <= nn) sum[x] = (sum[x] - v + mod) % mod, num[x]--, x += lowbit(x); }
    	void Add(int x, long long v) {while (x <= nn) sum[x] = (sum[x] + v) % mod, num[x]++, x += lowbit(x); }
    	long long Ask_Sum(int x) {
    		long long ret = 0;
    		while (x) ret = (ret + sum[x]) % mod, x -= lowbit(x);
    		return ret;
    	}
    	long long Ask_Num(int x) {
    		long long ret = 0;
    		while (x) ret = (ret + num[x]) % mod, x -= lowbit(x);
    		return ret;
    	}
    	long long ksm(long long a, int b) {
    		long long ret = 1;
    		while (b) {
    			if (b & 1) ret = (ret * a) % mod;
    			a = (a * a) % mod;
    			b >>= 1;
    		}
    		return ret;
    	}
    	void MAIN() {
    		StandardIO :: read(n); StandardIO :: read(k);
    		for (int i = 1; i <= n; i++) StandardIO :: read(h[i]), g[i] = h[i];
    		sort(g + 1, g + 1 + n);
    		nn = unique(g + 1, g + 1 + n) - g - 1;
    		for (int i = 1; i <= n; i++) h[i] = lower_bound(g + 1, g + 1 + nn, h[i]) - g;
    		ans = g[h[1]];
    		Add(h[1], g[h[1]]);
    		for (int i = 2; i <= n; i++) {
    			int fir = max(1, i - k);
    			if (fir > 1) Sub(h[fir - 1], g[h[fir - 1]]);
    			ans = (ans + (g[h[i]] * Ask_Num(h[i]) % mod - Ask_Sum(h[i]) + mod) % mod * ksm(min(i - 1, k), mod - 2) % mod) % mod;
    			Add(h[i], g[h[i]]);
    		}
    		StandardIO :: print(ans);
    	}
    }
    

    [Cnoi2020]线形生物

    近乎图上随机游走的板子。

    解法一

    先设每条边的出度为 (d_i),从 (i) 走到 (n+1) 期望走 (f_i) 步。则有 (f_i=frac{1}{d_i} imessum_{i ightarrow j}f_j+1)

    分析可得:(f_i=frac{1}{d_i} imes(f_{i+1}+sum_{i ightarrow j,j eq i+1}f_j)+1Rightarrow f_{i+1}=d_i imes(f_i-1)-sum_{i ightarrow j, j eq i+1}f_j)

    由此我们可以知道 (f_i) 一定是关于 (f_1) 的线性函数,即 (f_i=A_if_1+B_i)。显然 (A_1=1,B_1=0)

    (f_{n+1}=A_{n+1}f_1+B{n+1}=0 Rightarrow f_1=-frac{B_{n+1}}{A_{n+1}})

    所以我们现在要求 (A,B)。根据刚刚的递推式可以得出:

    (A_{i+1}f_1+B_{i+1}=d_i(A_if_1+B_i-1)-sum_{i ightarrow j}(A_jf_1+B_j)).

    (A_{i+1}=d_iA_i-sum_{i ightarrow j}A_j).

    (B_{i+1}=d_i(B_i-1)-sum_{i ightarrow j}B_j).

    然后由于返祖边只有 (m) 条,所以可以在 (O(n+m)) 的时间内解决。

    namespace Solve{
    	const long long mod = 998244353;
    	const int MAXN = 2000010;
    	static int id, n, m;
    	static long long d[MAXN], a[MAXN], b[MAXN];
    	vector<int> vec[MAXN];
    	long long ksm(long long x, long long y) {
    		long long ret = 1;
    		while (y) {
    			if (y & 1) ret = (ret * x) % mod;
    			x = (x * x) % mod;
    			y >>= 1;
    		}
    		return ret;
    	}
    	void MAIN() {
    		read(id); read(n); read(m);
    		for (int i = 1, u, v; i <= m; i++) {
    			read(u); read(v);
    			vec[u].push_back(v);
    			d[u]++;
    		}
    		for (int i = 1; i <= n; i++) d[i]++;
    		a[1] = 1, b[1] = 0;
    		for (int i = 1; i <= n; i++) {
    			a[i + 1] = d[i] * a[i] % mod;
    			b[i + 1] = d[i] * (b[i] - 1) % mod;
    			for (int j = 0; j < (int)vec[i].size(); j++) {
    				a[i + 1] = ((a[i + 1] - a[vec[i][j]]) % mod + mod) % mod;
    				b[i + 1] = ((b[i + 1] - b[vec[i][j]]) % mod + mod) % mod;
    			}
    		}
    		print(((-b[n + 1] % mod + mod) % mod * ksm(a[n + 1], mod - 2) % mod + mod) % mod);
    	}
    }
    

    解法二

    定义 (f_i) 代表从 (i)(i+1) 的期望步数。

    则有 (f_i=frac{1}{d_i}(1+sum_{i ightarrow j}sum_i-sum_{j-1}))。其中 (sum_i)(f) 的前缀和。再把 (f_i)(sum_i) 分离出来也可以做。

    最后答案是 (sum_{i=1}^{n}f_i)

  • 相关阅读:
    痞子衡嵌入式:并行NAND接口标准(ONFI)及SLC Raw NAND简介
    痞子衡嵌入式:恩智浦i.MX RT1xxx系列MCU启动那些事(6)- Bootable image格式与加载(elftosb/.bd)
    痞子衡嵌入式:恩智浦i.MX RT1xxx系列MCU启动那些事(5)- 再聊eFUSE及其烧写方法
    痞子衡嵌入式:恩智浦i.MX RT1xxx系列MCU启动那些事(4)- Flashloader初体验(blhost)
    Hystrix完整配置列表
    使用Redis实现UA池
    使用Redis实现延时任务(二)
    使用Redis实现延时任务(一)
    一文彻底理解Redis序列化协议,你也可以编写Redis客户端
    一个低级错误引发Netty编码解码中文异常
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/13703053.html
Copyright © 2011-2022 走看看