『模拟赛』CSP-S模拟11
Rank
地狱场,一般
A. 玩水 (water)
签。
发现
我写的好像是 dp?为每个点开一个大小为 26 的状态,表示从哪个字母转移而来的方案数,到该点的全部合法方案数即为
点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(register int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(register int (x) = (y); (x) >= (z); (x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
char ch = getchar();lx x = 0 , f = 1;
for(;ch<'0'||ch>'9';ch = getchar()) if(ch == '-') f = -1;
for(;ch>= '0' && ch<= '9';ch = getchar()) x = (x<<3) + (x<<1) + (ch^48);
return x*f;
}
#undef lx
#define qr qr()
#define pii pair<int, int>
#define pil pair<int, ll>
#define fi first
#define se second
#define int ll
const int Ratio = 0;
const int N = 1000 + 5;
int n, m;
int a[N][N][26];
char ch[N][N];
namespace Wisadel
{
short main()
{
freopen("water.in", "r", stdin) , freopen("water.out", "w", stdout);
// freopen(".err", "w", stderr);
int T = qr;
while(T--)
{
n = qr, m = qr;
fo(i, 1, n) fo(j, 1, m) fo(k, 0, 25) a[i][j][k] = 0;
fo(i, 1, n) fo(j, 1, m) cin >> ch[i][j];
a[1][1][0] = 1;
fo(i, 1, n) fo(j, 1, m)
{
int zc = 0;
if(i == 1 && j == 1) continue;
if(i == 1)
{
fo(k, 0, 25) zc = max(zc, a[i][j - 1][k]);
a[i][j][ch[i][j - 1] - 'a'] = zc;
}
else if(j == 1)
{
fo(k, 0, 25) zc = max(zc, a[i - 1][j][k]);
a[i][j][ch[i - 1][j] - 'a'] = zc;
}
else
{
if(ch[i - 1][j] == ch[i][j - 1])
{
fo(k, 0, 25) zc = max(zc, a[i - 1][j][k] + a[i][j - 1][k]);
a[i][j][ch[i - 1][j] - 'a'] = zc;
}
else
{
fo(k, 0, 25) zc = max(zc, a[i - 1][j][k]);
a[i][j][ch[i - 1][j] - 'a'] = zc;
zc = 0;
fo(k, 0, 25) zc = max(zc, a[i][j - 1][k]);
a[i][j][ch[i][j - 1] - 'a'] = zc;
}
}
}
int ok = 0;
fo(i, 0, 25) if(a[n][m][i] >= 3){ok = 1; break;}
if(ok) printf("1\n");
else printf("0\n");
}
return Ratio;
}
}
signed main(){return Wisadel::main();}
B. AVL 树
ex 题。
赛时头脑一热就全砸这题上了,假贪心 16pts,改掉唐错 21pts。
注意 AVL 树的性质,每个点的左右子树高度差
然后通过手模打表,发现树的深度与最小大小成斐波那契数列关系。
深度 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
大小 | 1 | 2 | 4 | 7 | 12 |
关系 | - | - |
更好理解的,相当于是将一棵深度为
再根据题目,要求求出字典序最小的方案。由于 AVL 树还是一棵二叉搜索树,所以我们直接按先序遍历贪心加点做就行。因为选中一个点则必定要选其所有父节点,因此中序遍历是一样的。
考虑选中某个点使树仍为 AVL 树,我们直接从该点开始向根节点跳。由于我们先遍历左子树后遍历右子树,因此左子树的方案在遍历右子树时是已确定了的,我们只用在一点为其父节点的左子节点时考虑另一边的情况,最好情况即为按斐波那契数列求出的最小大小。
在遍历过程中,我们会出现钦定某个节点的右子树大小的情况,为了不与之前的抉择冲突,我们记录每个子树合法深度下界的最大值。
当然还有细节处理。我们发现并不是所有样例的 AVL 树都是左偏的,会出现许多右子树深度大于左子树的情况。对于这种情况,我们考虑优先让左子树满足该点的合法深度下界,否则让右子树满足,以符合字典序最小的原则。并且为达到在某一深度最小,我们只需钦定一个子树的下界为该点下界,另一个为该点下界
然后就做完了,AVL 树的深度是接近
点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(register int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(register int (x) = (y); (x) >= (z); (x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
char ch = getchar();lx x = 0 , f = 1;
for(;ch<'0'||ch>'9';ch = getchar()) if(ch == '-') f = -1;
for(;ch>= '0' && ch<= '9';ch = getchar()) x = (x<<3) + (x<<1) + (ch^48);
return x*f;
}
#undef lx
#define qr qr()
#define pii pair<int, int>
#define pil pair<int, ll>
#define fi first
#define se second
#define int ll
const int Ratio = 0;
const int N = 5e5 + 5;
int n, k, rot, tot;
int siz[N];
int dep[N], mxdp[N], midp[N], h[N];
int ls[N], rs[N], fx[N];
bool yz[N];
namespace Wisadel
{
void Wpre(int u)
{
dep[u] = dep[fx[u]] + 1, mxdp[u] = dep[u];
if(ls[u]) Wpre(ls[u]), mxdp[u] = max(mxdp[u], mxdp[ls[u]]);
if(rs[u]) Wpre(rs[u]), mxdp[u] = max(mxdp[u], mxdp[rs[u]]);
}
bool Wck(int u)
{
int now = dep[u], ned = 0;
while(u)
{
if(!yz[u]) ned++;
now = max(now, h[u]);
if(u == ls[fx[u]])
ned += siz[max(now - 1, midp[rs[fx[u]]]) - dep[fx[u]]];
u = fx[u];
}
return ned <= k - tot;
}
void Wins(int u)
{
int now = max(h[u], dep[u]);
while(u)
{
h[u] = max(h[u], now);
if(!yz[u]) yz[u] = 1, tot++;
if(u == ls[fx[u]] && rs[fx[u]])
midp[rs[fx[u]]] = max(midp[rs[fx[u]]], h[u] - 1);
u = fx[u];
}
}
void Wsol(int u)
{
if(Wck(u)) Wins(u);
if(ls[u] && rs[u])
{
if(mxdp[ls[u]] < midp[u])
midp[ls[u]] = max(midp[ls[u]], midp[u] - 1),
midp[rs[u]] = max(midp[rs[u]], midp[u]);
else
midp[ls[u]] = max(midp[ls[u]], midp[u]),
midp[rs[u]] = max(midp[rs[u]], midp[u] - 1);
Wsol(ls[u]), Wsol(rs[u]);
}
else
{
if(ls[u]) midp[ls[u]] = max(midp[ls[u]], midp[u]), Wsol(ls[u]);
if(rs[u]) midp[rs[u]] = max(midp[rs[u]], midp[u]), Wsol(rs[u]);
}
}
short main()
{
freopen("avl.in", "r", stdin) , freopen("avl.out", "w", stdout);
// freopen(".err", "w", stderr);
n = qr, k = qr;
fo(i, 1, n)
{
int fa = qr;
if(fa == -1) rot = i;
else
{
if(i < fa) ls[fa] = i;
else rs[fa] = i;
fx[i] = fa;
}
}
siz[1] = 1, siz[2] = 2;
fo(i, 3, 30) siz[i] = siz[i - 1] + siz[i - 2] + 1;
Wpre(rot);
Wsol(rot);
fo(i, 1, n) printf("%d", yz[i]);
return Ratio;
}
}
signed main(){return Wisadel::main();}
C. 暴雨
阅读理解题。
先放个勾氏题面
什么是
什么是土地内部或边界?
什么是长度为
什么是一个点左右存在高度相同属于土地的点?
一个三维空间几何体积水让您说的这么勾氏也是很牛了。
南平。不过题本身也很勾氏,看懂了也就打个暴力。
这种全局性问题一般是考虑 dp 吧。设
发现一块地两侧都是能积水的,非常不好转移,于是我们分开求,正着求一遍倒着求一遍,最大值一闭一开最后合并即可。
由于 long long
只能开过程量,不然会 MLE。
采用映射的方法,记录到第
:
- 相反:
最后枚举最高点乘法原理拼接即可。
点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(register int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(register int (x) = (y); (x) >= (z); (x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
char ch = getchar();lx x = 0 , f = 1;
for(;ch<'0'||ch>'9';ch = getchar()) if(ch == '-') f = -1;
for(;ch>= '0' && ch<= '9';ch = getchar()) x = (x<<3) + (x<<1) + (ch^48);
return x*f;
}
#undef lx
#define qr qr()
#define pii pair<int, int>
#define pil pair<int, ll>
#define fi first
#define se second
const int Ratio = 0;
const int N = 25000 + 5;
const int mod = 1e9 + 7;
int n, m;
int a[N], id[N];
multiset<int>::iterator it;
ll ans;
namespace Wisadel
{
struct rmm
{
int f[N][26][26][2], val[N][26], cnt[N];
multiset<int> st;
unordered_map<int,int> mp[N];
void Wsol()
{
st.insert(0);
fo(i, 1, n)
{
st.insert(a[i]);
it = st.end(); it--;
for(; ; it--)
{
mp[i][*it] = ++cnt[i];
val[i][cnt[i]] = *it;
if(it == st.begin() || cnt[i] == m + 1) break;
}
}
f[0][1][0][0] = 1, val[0][cnt[0] = 1] = 0;
fo(i, 0, n - 1) fo(j, 1, cnt[i]) fo(k, 0, m) fo(p, 0, 1)
if(f[i][j][k][p])
{
int zc, zt;
if(val[i][j] >= a[i + 1])
{
zc = mp[i + 1][val[i][j]], zt = p ^ (val[i][j] - a[i + 1] & 1);
f[i + 1][zc][k][zt] = (f[i + 1][zc][k][zt] + f[i][j][k][p]) % mod;
zt = p ^ (val[i][j] & 1);
if(k < m) f[i + 1][zc][k + 1][zt] = (f[i + 1][zc][k + 1][zt] + f[i][j][k][p]) % mod;
}
else
{
zc = mp[i + 1][a[i + 1]], zt = p ^ (val[i][j] & 1);
f[i + 1][zc][k][p] = (f[i + 1][zc][k][p] + f[i][j][k][p]) % mod;
zc = mp[i + 1][val[i][j]];
if(k < m) f[i + 1][zc][k + 1][zt] = (f[i + 1][zc][k + 1][zt] + f[i][j][k][p]) % mod;
}
}
}
} Q, H;
short main()
{
freopen("rain.in", "r", stdin) , freopen("rain.out", "w", stdout);
// freopen(".err", "w", stderr);
n = qr, m = qr;
fo(i, 1, n) a[i] = qr, id[i] = i;
Q.Wsol(); reverse(a + 1, a + 1 + n);
H.Wsol(); reverse(a + 1, a + 1 + n);
sort(id + 1, id + n + 1, [](int aa, int bb){return a[aa] > a[bb];});
fo(pop, 1, n)
{
if(a[id[pop]] < a[id[m + 1]]) break;
int i = id[pop], vv = a[i];
fu(j, Q.cnt[i - 1], 1)
{
if(Q.val[i - 1][j] > vv) break;
fu(k, H.cnt[n - i], 1)
{
if(H.val[n - i][k] >= vv) break;
fo(u, 0, m)
ans = (ans + 1ll * Q.f[i - 1][j][u][0] * H.f[n - i][k][m - u][0] % mod) % mod,
ans = (ans + 1ll * Q.f[i - 1][j][u][1] * H.f[n - i][k][m - u][1] % mod) % mod;
}
}
}
printf("%lld\n", ans);
return Ratio;
}
}
signed main(){return Wisadel::main();}
D. 置换
手题,不很会。
E. 传统题
炉石题组合推式子题,不很会。
末
因为公告所以开场磕了会 T4 发现不会然后转战 T1,还好在 huge 加题前就扔了,赢(?)。
然后由于 T3 逆天题面和 T4 不写,打完 T5 暴力就全磕 T2 了,结果还是假了,甚至比暴力分低(
还以为你们能比学长强点
还是算了😅😅
完结撒花~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探