2025-11-24 ZYZ28-NOIP模拟赛-Round8 hetao1733837的record
2025-11-24 ZYZ28-NOIP模拟赛-Round8 hetao1733837的record
比赛链接:ZYZ28-NOIP模拟赛-Round8
A.number
原题链接:08-A
题面
题目描述
小 Z 有一个长度为$n$的序列$A = {a_1, a_2, \cdots, a_n}$。
如果对于数$a_i$,在下标为$[1, i - 1]$的区间内如果存在三个数使得这三个数这和恰好等于$a_i$,那么称这个数为好数。
小 Z 想知道这个数列中共有多少个这样的好数。
注意:数列中的数字可以重复使用。
输入格式
从 number.in 文件读入数据。
第一行输入一个正整数$n$表示数列的长度。
第二行输入$n$个整数$a_1, a_2, \cdots, a_n$。
输出格式
输出到 number.out 文件。
输出一行一个正整数表示好数的个数。
input1
2
1 3
output1
1
input2
6
1 2 3 5 7 10
output2
4
input3
3
-1 2 0
output3
1
分析
评橙?反正我切了。发现三维不好做,那先考虑两维,变为前$i-1$个数的和,同时反向枚举当前$a_i$与前面的差,拿个桶存一下没了。
正解
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, a[N];
bool vis[N << 1];
int main(){
freopen("number.in", "r", stdin);
freopen("number.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++){
cin >> a[i];
}
int cnt = 0;
if (a[1] + a[1] + N >= 0 && a[1] + a[1] + N < (N << 1))
vis[a[1] + a[1] + N] = 1;
for (int i = 2; i <= n; i++){
bool flag = 0;
for (int j = 1; j < i; j++){
if (a[i] - a[j] + N >= 0 && a[i] - a[j] + N < (N << 1)){
if (vis[a[i] - a[j] + N]){
flag = 1;
break;
}
}
}
if (flag)
cnt++;
for (int j = 1; j <= i; j++){
if (a[i] + a[j] + N >= 0 && a[i] + a[j] + N < (N << 1))
vis[a[i] + a[j] + N] = 1;
}
}
cout << cnt;
}
B.sos
提交链接:08-B
题面
题目描述
当危机四伏,人们常常通过密信来传递求救信息。
在很多很多年以后,历史学家小Z翻阅古籍发现了一些模糊的密信,这些密信其中一些文字已经难以辨认,但在可以辨认的文字中,小Z发现了他记忆中的符号 sos,这是国际通用的求救信号。小 Z 深知这个信号的重要性,他想知道原始的密信上有多少种可能会出现三次及以上 sos 的组合方式。
小Z知道,这些密信可能隐藏着无数种解读方式,但他只关心那些包含三次及以上 sos 的组合(注意 sosos 只能算出现一次)。
他希望通过计算这些组合的数量,来更深入地了解这些密信背后的故事和含义,这样的合法的密信可能有很多,小 Z 只想知道这些合法组合对 $10^9+7$ 取模的结果即可。
输入格式
从 sos.in 文件读入数据。
第一行输入的是整数$n$表示一封密信的长度,请注意密信只可能包含大写的英文字母。
输出格式
输出到 sos.out 文件。
一个数字,表示有多少种字符串可能出现了三个及以上 sos。
input1
10
output1
104
input2
174
output2
122889315
input3
17711
output3
665739088
input4
308235
output4
701079308
分析
居然是把矩阵拿下来得出神秘式子吗?我并不会啊。
还是循规蹈矩一点,开始 dp 吧!
设 $f_{i,j,k}$ 表示当前为第 i 位,匹配了 j 个 sos,匹配进度为 s,o,s 中的第几个的方案数。
转移显然,
$f_{i+1,0,0}\leftarrow f_{i,0,0}\times 25$
$f_{i+1,0,0}\leftarrow f_{i,0,1}\times 24$
$f_{i+1,0,0}\leftarrow f_{i,0,2}\times 25$
$f_{i+1,1,0}\leftarrow f_{i,1,0}\times 25$
$f_{i+1,1,0}\leftarrow f_{i,1,1}\times 24$
$f_{i+1,1,0}\leftarrow f_{i,1,2}\times 25$
$f_{i+1,2,0}\leftarrow f_{i,2,0}\times 25$
$f_{i+1,2,0}\leftarrow f_{i,2,1}\times 24$
$f_{i+1,2,0}\leftarrow f_{i,2,2}\times 25$
$f_{i+1,0,1}\leftarrow f_{i,0,1}$
$f_{i+1,1,1}\leftarrow f_{i,1,1}$
$f_{i+1,2,1}\leftarrow f_{i,2,1}$
$f_{i+1,0,1}\leftarrow f_{i,0,0}$
$f_{i+1,0,2}\leftarrow f_{i,0,1}$
$f_{i+1,1,0}\leftarrow f_{i,0,2}$
$f_{i+1,1,1}\leftarrow f_{i,1,0}$
$f_{i+1,1,2}\leftarrow f_{i,1,1}$
$f_{i+1,2,0}\leftarrow f_{i,1,2}$
$f_{i+1,2,1}\leftarrow f_{i,2,0}$
$f_{i+1,2,2}\leftarrow f_{i,2,1}$
$ans\leftarrow f_{i,2,2} \times 26^{n-(i+1)}$
🤮
正解
#include<bits/stdc++.h>
using namespace std;
const long long mod=1e9+7;
long long qpow(long long x,long long a){
long long res=1;
while(a){
if(a&1) res=res*x%mod;
x=x*x%mod;
a>>=1;
}
return res;
}
int n;
long long dp[1000010][3][3];
int main(){
freopen("sos.in","r",stdin);
freopen("sos.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;
dp[0][0][0]=1;
long long ans=0;
for(int i=0;i<n;i++){
(dp[i+1][0][0]+=dp[i][0][0]*25)%=mod;
(dp[i+1][0][0]+=dp[i][0][1]*24)%=mod;
(dp[i+1][0][0]+=dp[i][0][2]*25)%=mod;
(dp[i+1][1][0]+=dp[i][1][0]*25)%=mod;
(dp[i+1][1][0]+=dp[i][1][1]*24)%=mod;
(dp[i+1][1][0]+=dp[i][1][2]*25)%=mod;
(dp[i+1][2][0]+=dp[i][2][0]*25)%=mod;
(dp[i+1][2][0]+=dp[i][2][1]*24)%=mod;
(dp[i+1][2][0]+=dp[i][2][2]*25)%=mod;
(dp[i+1][0][1]+=dp[i][0][1])%=mod;
(dp[i+1][1][1]+=dp[i][1][1])%=mod;
(dp[i+1][2][1]+=dp[i][2][1])%=mod;
(dp[i+1][0][1]+=dp[i][0][0])%=mod;
(dp[i+1][0][2]+=dp[i][0][1])%=mod;
(dp[i+1][1][0]+=dp[i][0][2])%=mod;
(dp[i+1][1][1]+=dp[i][1][0])%=mod;
(dp[i+1][1][2]+=dp[i][1][1])%=mod;
(dp[i+1][2][0]+=dp[i][1][2])%=mod;
(dp[i+1][2][1]+=dp[i][2][0])%=mod;
(dp[i+1][2][2]+=dp[i][2][1])%=mod;
(ans+=dp[i][2][2]*qpow(26,n-(i+1))%mod)%=mod;
}
cout<<ans;
}
AeeE5x 是怎么在场上吃出来这个的?
所以,我们对 DP 的理解不够?
C.balloon
前置知识:退背包。
原题链接:08-C
题面
题目描述
小 Z 在暑期举办了一个集训营,最后一次集训营测试,准备让孩子们体验一下 ACM 赛制。众所周知,在 ACM 赛制中,每通过一个题,主办方就会给通过的队伍送一个气球,其中第一个通过某题的队伍还会获得一个一血气球。
小 Z 从 ACM 赛制中想到了一个商机,他在经常举办 ACM 比赛的地方开了一个卖气球的小店。小 Z 的气球店只会售卖两种气球:一血气球和普通气球。每种气球的数量是无限的。
今天,小 Z 的气球店来了 $n$ 位顾客,顾客编号为 $1, 2, \cdots, n$,其中第 $i$ 位顾客最多购买 $a_i$ 个一血气球或 $b_i$ 个普通气球。但是,顾客只会购买一种气球,要么买一血气球要么买彩色气球,并且至少买一个气球。
但是,顾客可能有时候因为不同的想法而改变购买气球的需求。而小 Z 是一个强迫症,他强制要求顾客中至少有 $c$ 个人购买他的一血气球,购买一血气球的数量并不做要求。
现在顾客会更改他们的需求 $q$ 次,问,每次顾客的需求更改后小 Z 有多少种方案卖出他的气球。
- 两种不同的方案,当且仅当 $n$ 个顾客中有一个顾客买到的气球的种类不同或者数量不同。
输入格式
从 balloon.in 文件读入数据。
第一行两个整数 $n$ 和 $c$,分别表示顾客数量和小 Z 要求买一血气球的人数。
第二行 $n$ 个整数 $a_1, a_2, \cdots, a_n$。
第三行 $n$ 个整数 $b_1, b_2, \cdots, b_n$。
第四行一个整数 $q$ 表示顾客更改的需求次数。
接下来 $q$ 行,每行三个整数 $t, x, y$,分别表示第 $i$ 个人,他的需求变成了至多购买 $x$ 个一血气球以及 $y$ 个普通气球。
输出格式
输出到 balloon.out 文件。
对于每次更改需求,输出一个正整数,表示方案数,答案对 $10^9+7$ 取模。
分析
对于30分,跑 q 次背包即可。啥?这题是个背包?重新读题!
对于50分,开 O2 优化的话,通过线段树维护动态 DP 即可。若不开 O2,则使用容斥进行卡常。
对于满分,就要使用退背包了 (T4 好像也是这么要求的)。
首先普及一下退背包。做一下 LG4141 学习。
其实就是多记录一维?何意味。感觉和换根 DP 之类的比较像。
好的,开始看《星轨》题解!
这不是乘法原理乱做吗?我是堂堂吗?哦?难道时间跑不下?猜对了,(●'◡'●)
好的,开始正解。正难则反!我们考虑不满足 c 的方案数,利用广义 hxf 容斥即可解决此题。
但是,还是一个动态 DP 问题,不过成了会做的背包。
设 $tot$ 为总方案数,$x$,$y$ 为修改后的第 $i$ 个人的 $a_i,b_i$。$inv(x)$ 为 $x$ 的乘法逆元。
$$
tot=\prod\limits_{i=1}^{n}a_i+b_i \Longrightarrow tot'=\frac{tot}{a_i+b_i}\times(x + y) = tot \times inv(a_i+b_i)\times (x + y)
$$
设 $f$ 为考虑该人前的 DP 数组,g 为考虑该人之后的 DP 数组。
$$
g_i=\left{\begin{matrix} f_i \times b,i=0\
f_{i-1}\times a+f_i\times b,i>0
\end{matrix}\right.
$$
$$
f_i=\left{\begin{matrix} g_i \times inv(b),i=0\
(g_i-f_{i-1}\times a)\times inv(b),i>0
\end{matrix}\right.
$$
正解
#include <bits/stdc++.h>
#define mod 1000000007
using namespace std;
const int N = 1000005, C = 25;
int qpow(int a, int b){
int res = 1;
while (b){
if (b & 1)
res = 1ll * res * a % mod;
a = 1ll * a * a % mod;
b >>= 1;
}
return res;
}
int a[N], b[N], n, c, q, f[C], tot;
int main(){
freopen("balloon.in", "r", stdin);
freopen("balloon.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> c;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= n; i++)
cin >> b[i];
tot = f[0] = 1;
for (int i = 1; i <= n; i++){
for (int j = c - 1; j > 0; j--)
f[j] = (1ll * f[j - 1] * a[i] % mod + 1ll * f[j] * b[i] % mod) % mod;
f[0] = 1ll * f[0] * b[i] % mod;
tot = 1ll * tot * (a[i] + b[i]) % mod;
}
cin >> q;
while (q--){
int p, x, y;
cin >> p >> x >> y;
tot = 1ll * tot * qpow(a[p] + b[p], mod - 2) % mod * (x + y) % mod;
int invb = qpow(b[p], mod - 2);
f[0] = 1ll * f[0] * invb % mod;
for (int i = 1; i < c; i++)
f[i] = 1ll * (f[i] - 1ll * f[i - 1] * a[p] % mod + mod) % mod * invb % mod;
for (int i = c - 1; i > 0; i--)
f[i] = (1ll * f[i - 1] * x % mod + 1ll * f[i] * y % mod) % mod;
f[0] = 1ll * f[0] * y % mod;
a[p] = x;
b[p] = y;
int ans = tot;
for (int i = 0; i < c; i++)
ans = (ans - f[i] + mod) % mod;
cout << ans << '\n';
}
return 0;
}
D.tree
原题链接:08-D
分析
补这题纯属感兴趣,说的有点奇怪哈😁
先想50分做法,利用中心的 一条判则:以一个节点为根重构树,如果每个子树大小都不超过$\frac{n}{2}$则为重心。
int n, sz[N], w[N], c[2];
vector<int> e[N];
void dfs(int u, int fa){
sz[u] = 1;
w[u] = 0;
for (auto v : e[u]){
if (v == fa)
continue;
dfs(v, u);
sz[u] += sz[v];
w[u] = max(w[u], sz[v]);
}
w[u] = max(w[u], n - sz[u]);
if (w[u] <= n / 2){
c[c[0] != 0] = u;
cnt++;
}
}
若c[1]=0,则中心仅有 1 个,反之有 2 个。
若有两个重心,不妨设为 $a, b$,将两者连边断开变成两棵树,其根分别为 $a,b$。
设 $f_{u,i}$表示以 $u$ 为根的子树内包含点 $u$ 且有 $i$ 个节点的连通块的个数,可以拿树形背包做。答案即为$\sum\limits_{i=1}^{\max(sz_u, sz_v)}f_{u,i}\times f_{v, i}$。
若有一个重心 $a$,设 $g_{i,j}$ 表示以包含 $a$ 且大小为 $i$ 的一个连通块,其中最大子树大小为 $j$ 的连通块个数,答案即为 $\sum\limits_{1\le i\le n,2j\le i}^{}g_{i,j}$。
但是100分做法和这个关系似乎并不大。
注意到 $f_{u,i}$表示以 $u$ 为根的子树内包含点 $u$ 且有 $i$ 个节点的连通块的个数,那么,只需要其中最大子树大小$>\frac{i}{2}$个数即可。
如果一个子树 $t$ 大小为 $k$,其中 $k>\frac{i}{2}$,那么 $t$只有一个,这时一次退背包把 $t$ 的贡献删掉。
正解
#include <bits/stdc++.h>
#define int long long
#define mod 10007
using namespace std;
const int N = 5005;
int n, sz[N], w[N], c[2], f[N][N], g[N][N], cnt;
vector<int> e[N];
void dfs(int u, int fa){
sz[u] = 1;
w[u] = 0;
for (auto v : e[u]){
if (v == fa)
continue;
dfs(v, u);
sz[u] += sz[v];
w[u] = max(w[u], sz[v]);
}
w[u] = max(w[u], n - sz[u]);
if (w[u] <= n / 2){
c[c[0] != 0] = u;
cnt++;
}
}
void dfs_dp(int u, int fa){
sz[u] = 1;
f[u][0] = f[u][1] = 1;
for (auto v : e[u]){
if (v == fa)
continue;
dfs_dp(v, u);
sz[u] += sz[v];
for (int i = sz[u]; i; i--){
for (int j = 1; j <= sz[v] && j <= i - 1; j++){
f[u][i] = (f[u][i] + f[v][j] * f[u][i - j]) % mod;
}
}
}
}
void solve1(){
int u = c[0], ans = 0;
dfs_dp(u, 0);
for (int i = 1; i <= n; i++)
ans = (ans + f[u][i]) % mod;
for (auto v : e[u]){
for (int i = 1; i <= n; i++){
g[v][i] = f[u][i];
for (int k = 1; k <= min(i, sz[v]); k++){
g[v][i] = (g[v][i] - g[v][i - k] * f[v][k] % mod + mod) % mod;
}
}
}
for (int i = 1; i <= n; i++){
for (auto v : e[u]){
for (int k = (i + 1) / 2; k <= i && k <= sz[v]; k++){
ans = (ans - f[v][k] * g[v][i - k] % mod + mod) % mod;
}
}
}
cout << ans;
}
void solve2(){
dfs_dp(c[0], c[1]);
dfs_dp(c[1], c[0]);
int ans = 0;
for (int i = 1; i <= n; i++)
ans = (ans + f[c[0]][i] * f[c[1]][i] % mod) % mod;
cout << ans;
}
signed main(){
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
cin >> n;
for (int i = 1; i <= n - 1; i++){
int u, v;
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1, 0);
if (c[1])
solve2();
else
solve1();
}
完结撒花*★,°*:.☆( ̄▽ ̄)/$:.°★ 。
posted on 2025-11-24 20:15 hetao1733837 阅读(9) 评论(0) 收藏 举报
浙公网安备 33010602011771号