2021.10.4 比赛题整理
2021.10.4 2021CSPJ初二初一冲刺七
总结
炸了炸了。。T3 半天做了个寂寞。
对算法不熟悉。
T1:简单思维题;
T2:KMP nxt 数组的运用;
T3:二分 + 图,代码实现可用并查集;
T4:四维树形 dp。
T1
题意
设 a 0 ← 1 a_0 \gets 1 a0←1, a n ← a i + a j a_n \gets a_i + a_j an←ai+aj(i,j 在 [ 0 , n − 1 ) [0,n-1) [0,n−1) 范围内随机)。
求对于给定的 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) an←2(n+1)(a0+a1+⋯+an−1)÷(2n(n+1)+n)
最后可以推出 a n ← n + 1 a_n \gets n+1 an←n+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,s2⋯snxti 前缀中我们已经算过添了 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 个监听器。限定每个节点最多只能放一个监听器,监听器可以监听该点所有相邻的节点,且无法监听该点。
要求必须要将所有监听器放完,求不同放置方法的数量。
题解
很明显的树形 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——
阳 和 启 蛰 , 枯 木 逢 春 。 阳和启蛰,枯木逢春。 阳和启蛰,枯木逢春。

浙公网安备 33010602011771号