2021.10.4 比赛题整理

2021.10.4 2021CSPJ初二初一冲刺七

链接集合

总结

炸了炸了。。T3 半天做了个寂寞。

对算法不熟悉。

T1:简单思维题;

T2:KMP nxt 数组的运用;

T3:二分 + 图,代码实现可用并查集;

T4:四维树形 dp。

T1

题意

a 0 ← 1 a_0 \gets 1 a01 a n ← a i + a j a_n \gets a_i + a_j anai+aj(i,j 在 [ 0 , n − 1 ) [0,n-1) [0,n1) 范围内随机)。

求对于给定的 n n n a n a_n an 的期望值为多少。

题解

期望值,即所有可能值的平均值。

归纳答案,可得:

a n ← ( n + 1 ) ( a 0 + a 1 + ⋯ + a n − 1 ) 2 ÷ ( n ( n + 1 ) 2 + n ) a_n \gets \dfrac {(n +1)(a_0+a_1+\cdots+a_{n-1})}{2} \div (\dfrac {n(n + 1)}{2}+n) an2(n+1)(a0+a1++an1)÷(2n(n+1)+n)

最后可以推出 a n ← n + 1 a_n \gets n+1 ann+1 的结论。

这题确实不难。

记得开 long long

代码

#include<bits/stdc++.h>
using namespace std;

#define int long long
int t, n;

inline int read ()
{
	int x = 1, s = 0;
	char ch = getchar ();
	while (ch < '0' or ch > '9'){if (ch == '-') x = -1; ch = getchar ();}
	while (ch >= '0' and ch <= '9') s = s * 10 + ch - '0', ch = getchar ();
	return x * s;
}

signed main ()
{
	t = read ();
	while (t--)
	{
		n = read ();
		printf ("%lld\n", n + 1);
	}
	return 0;
}

T2

题意

给定一字符串,求所有长度为偶数的前缀在母串中出现次数之和。

题解

“字符串”、“前缀”这些关键词可以映射到 KMP 算法上。

同时也运用了递推的思想,用前面的答案推出后面的答案。

定义 f i f_i fi 为上一前缀加上下标为 i 的字符之后增加了的符合条件的字符串。

而在之前的 s 1 , s 2 ⋯ s n x t i s_1,s_2 \cdots s_{nxt_i} s1,s2snxti 前缀中我们已经算过添了 s n x t i s_{nxt_i} snxti 之后增加的答案数。所以我们可以直接给 f i f_i fi 加上 f n x t i f_{nxt_i} fnxti

若此时 i 为偶数,则当前前缀本身符合条件,另外给 f i f_i fi 加一即可。

代码

#include<bits/stdc++.h>
using namespace std;

const int maxn = 200005;
int n, nxt[maxn], f[maxn], ans;
char s[maxn];

int main ()
{
	scanf ("%s", s + 1);
	n = strlen (s + 1);
	int j = 0;
	for (int i = 2; i <= n; ++i)
	{
		if (i % 2 == 0) f[i]++;
		while (j and s[j + 1] != s[i]) j = nxt[j];
		if (s[j + 1] == s[i]) j++;
		nxt[i] = j;
		f[i] += f[j], ans += f[i];
	}
	printf ("%d\n", ans);
	return 0;
}

T3

题意

给定一张有 n 个节点,m 条边的无向图(可能有环或重边),对于每个节点,有 l 和 r,定义在经过该节点后,只能携带于 l 和 r 之间的数。

从编号为 1 的节点出发(初始时携带了所有自然数),到达编号为 n 的节点,求最后能携带最多的节点。

若节点数相同,输出字典序小的那一组。

题解

一道夹道之樱,忆回小六寒集的时候。。我赛时还是不会做。 又做了一遍。

二分 + 图。

二分本质:

在数轴上找到一个临界点,即点的左边都可以满足条件,右边都不可以满足条件(或左边不可以,右边可以)。

在这道题中,r 值是有单调性的。即有一临界点,使得比它小的 r 值无法满足,比它大的 r 值可以满足。

但是,l 不具有单调性。例如,当 l 值为 1 时可以满足,l 值为 5 时可以满足,但是 l 值为 3 时能否满足是无法直接断定的。

所以,r 的值可以通过二分得到,l 的值只能枚举而得。

以上是思路。

但实现的时候我们可以用并查集优化:

我们从小到大枚举 l 的值,在此基础上,我们按 r 的值从大到小枚举每一条边。

保证每一条使用了的边的 e i . l e_i.l ei.l 的值比 l 的值小。

这里就运用了二分的思想。

每用一条边,就用并查集维护,如果 f 1 = f n f_1 = f_n f1=fn,就记录答案,最终输出最佳答案即可。

代码

#include<bits/stdc++.h>
using namespace std;

#define rint register int
const int inf = 2e6 + 5, maxn = 1005;
int n, m;
int cnt, hd[maxn];
struct node{
	int s, t;
	int l, r;
}e[maxn * 4];
int f[maxn];
int ansl, num;
int recl[maxn * 4];

inline int read ()
{
	int x = 1, s = 0;
	char ch = getchar ();
	while (ch < '0' or ch > '9'){if (ch == '-') x = -1; ch = getchar ();}
	while (ch >= '0' and ch <= '9') s = s * 10 + ch - '0', ch = getchar ();
	return x * s;
}

inline bool cmp (node a, node b)
{
	return a.r == b.r ? a.l < b.l : a.r > b.r;
}

inline void init ()
{
	for (rint i (1); i <= n; ++i) f[i] = i;
}

inline int find (int x)
{
	return f[x] == x ? x : find (f[x]);
}

int main ()
{
	n = read (), m = read ();
	for (rint i (1); i <= m; ++i) e[i].s = read (), e[i].t = read (), e[i].l = read (), e[i].r = read (), recl[i] = e[i].l;
	sort (e + 1, e + m + 1, cmp);
	sort (recl + 1, recl + m + 1);
	for (rint i (1); i <= m; ++i)
	{
		init ();
		bool flag = 0;
		int tl = recl[i], tr = inf;
		for (rint j (1); j <= m; ++j)
		{
			if (e[j].l > tl) continue;
			int u = find (e[j].s), v = find (e[j].t);
			if (u != v)
			{
				f[u] = v;
				tr = min (tr, e[j].r);
				if (find (1) == find (n)) 
				{
					flag = 1;
					break;
				}
			}
		}
		if (flag and num < tr - tl + 1) ansl = tl, num = tr - tl + 1;
	}
	printf ("%d\n", num);
	for (int i = ansl; i <= ansl + num - 1; i++) printf ("%d ", i); 
	return 0;
}

T4

题意

给定一棵 n n n 个节点的无向树和 k k k 个监听器。限定每个节点最多只能放一个监听器,监听器可以监听该点所有相邻的节点,且无法监听该点。

要求必须要将所有监听器放完,求不同放置方法的数量。

P4516 [JSOI2018]潜入行动

题解

很明显的树形 dp。

背包树形 dp。

信息从儿子节点传到父亲节点,每次枚举监听器数量再赋值。

定义 d p [ i ] [ k ] [ 0 / 1 ] [ 0 / 1 ] dp[i][k][0/1][0/1] dp[i][k][0/1][0/1] 为根节点为 i 的子树在使用了 k 个监听器后含(或不含)i 全亮,且当前节点 i 安装(或没有安装)监听器。

0 表示包含了节点 i,1 则表示不包含;0 表示 i 节点没有安装监听器,1 则表示安装了。

代码

#include<bits/stdc++.h>
using namespace std;

//#define int long long
const int maxn = 1e5 + 5;
const int mod = 1000000007;
int n, k;
int cnt, hd[maxn];
struct node{
	int to, nxt;
}e[maxn * 2];
int dp[maxn][105][2][2], f[105][2][2];
int siz[maxn];

inline void add (int u, int v)
{
	e[++cnt].to = v, e[cnt].nxt = hd[u];
	hd[u] = cnt;
}

inline void dfs (int u, int fa)
{
	dp[u][0][0][0] = dp[u][1][0][1] = 1;
	siz[u] = 1;
	for (int i = hd[u]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		if (v == fa) continue;
		dfs (v, u);
		memset (f, 0, sizeof f);
		for (int j = 0; j <= min (siz[u], k); ++j)
		{
			for (int l = 0; l <= min (k - j, siz[v]); ++l)
			{
				f[j + l][0][0] = (f[j + l][0][0] + 1ll * dp[u][j][0][0] * dp[v][l][1][0]) % mod;
				f[j + l][0][1] = (f[j + l][0][1] + 1ll * dp[u][j][0][1] * (dp[v][l][0][0] + dp[v][l][1][0])) % mod;
				f[j + l][1][0] = (f[j + l][1][0] + 1ll * dp[u][j][1][0] * (dp[v][l][1][1] + dp[v][l][1][0]) + 1ll * dp[u][j][0][0] * dp[v][l][1][1]) % mod;
				f[j + l][1][1] = (f[j + l][1][1] + 1ll * dp[u][j][0][1] * (dp[v][l][0][1] + dp[v][l][1][1]) + dp[u][j][1][1] * (1ll * dp[v][l][0][0] + dp[v][l][1][0] + dp[v][l][0][1] + dp[v][l][1][1])) % mod;
			} 
		}
		memcpy (dp[u], f, sizeof f);
		siz[u] += siz[v];
	}
}

int main ()
{
	scanf ("%d %d", &n, &k);
	for (int i = 1; i < n; ++i) 
	{
		int u, v;
		scanf ("%d %d", &u, &v);
		add (u, v), add (v, u);
	}
	dfs (1, 0);
	printf ("%d\n", (dp[1][k][1][1] + dp[1][k][1][0]) % mod);
	return 0;
}

—— E n d End End——

阳 和 启 蛰 , 枯 木 逢 春 。 阳和启蛰,枯木逢春。

posted @ 2022-03-25 07:25  pldzy  阅读(32)  评论(0)    收藏  举报