『模拟赛』CSP-S模拟11
Rank
地狱场,一般
A. 玩水 (water)
签。
发现 \(n\le 1000\),\(T\le 10\),\(\mathcal{O(Tn^2)}\) 可过,所以简单考虑。
我写的好像是 dp?为每个点开一个大小为 26 的状态,表示从哪个字母转移而来的方案数,到该点的全部合法方案数即为 \(\max_{i\in\left[0,25\right]}\ cnt_i\)。由于是只能往下或往右走,因此一点最多由两点转移。由什么字母转移就加到什么状态上就行。特殊处理由两个相同字母转移的,此时合法方案数应为 \(\max_{i\in\left[0,25\right]}\ cnt_{a1,i}+cnt_{a_2,i}\)。然后就做完了,复杂度 \(\mathcal{O(Tn^2)}\)。
点击查看代码
#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 树的性质,每个点的左右子树高度差 \(\le 1\),并不要求同一层全选/全不选,可以通过一些智慧的方式来使得树更加偏于一方,如图:
然后通过手模打表,发现树的深度与最小大小成斐波那契数列关系。
深度 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
大小 | 1 | 2 | 4 | 7 | 12 |
关系 | - | - | \(f_1+f_2+1\) | \(f_2+f_3+1\) | \(f_3+f_4+1\) |
更好理解的,相当于是将一棵深度为 \(d-1\) 的最小 AVL 树与一棵深度为 \(d-2\) 的最小 AVL 树用一个根节点连接在一起形成了一棵深度为 \(d\) 的最小 AVL 树。
再根据题目,要求求出字典序最小的方案。由于 AVL 树还是一棵二叉搜索树,所以我们直接按先序遍历贪心加点做就行。因为选中一个点则必定要选其所有父节点,因此中序遍历是一样的。
考虑选中某个点使树仍为 AVL 树,我们直接从该点开始向根节点跳。由于我们先遍历左子树后遍历右子树,因此左子树的方案在遍历右子树时是已确定了的,我们只用在一点为其父节点的左子节点时考虑另一边的情况,最好情况即为按斐波那契数列求出的最小大小。
在遍历过程中,我们会出现钦定某个节点的右子树大小的情况,为了不与之前的抉择冲突,我们记录每个子树合法深度下界的最大值。
当然还有细节处理。我们发现并不是所有样例的 AVL 树都是左偏的,会出现许多右子树深度大于左子树的情况。对于这种情况,我们考虑优先让左子树满足该点的合法深度下界,否则让右子树满足,以符合字典序最小的原则。并且为达到在某一深度最小,我们只需钦定一个子树的下界为该点下界,另一个为该点下界 \(-1\) 即可。不用担心漏选,因为如果还有剩余我们会先遍历它。
然后就做完了,AVL 树的深度是接近 \(\log n\) 的,因此我们的复杂度是 \(\mathcal{O(n\log n)}\) 的。
点击查看代码
#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. 暴雨
阅读理解题。
先放个勾氏题面
什么是 \(1\times N\) 的矩阵?什么是 \(1\times 1\) 的土地?
什么是土地内部或边界?
什么是长度为 \(N\) 的两边?
什么是一个点左右存在高度相同属于土地的点?
一个三维空间几何体积水让您说的这么勾氏也是很牛了。
南平。不过题本身也很勾氏,看懂了也就打个暴力。
这种全局性问题一般是考虑 dp 吧。设 \(f_{i,j,k,0/1}\) 表示到第 \(i\) 块土地,最大高度为 \(j\) 且之后有不低于它的土地,拆了 \(k\) 块后积水体积为奇/偶的方案数。
发现一块地两侧都是能积水的,非常不好转移,于是我们分开求,正着求一遍倒着求一遍,最大值一闭一开最后合并即可。
由于 \(k\le 25\),所以只需记录前 \(k+1\) 高的土地的相关方案数就够了,空间复杂度是 \(\mathcal{O(n\times 25^2\times 2\times 2)}\),long long
只能开过程量,不然会 MLE。
采用映射的方法,记录到第 \(i\) 块前 \(k+1\) 高度的土地和位置,然后考虑转移,分两种情况:
- \(v_{i,j}\ge a_{i+1}\):
- 相反:
最后枚举最高点乘法原理拼接即可。
点击查看代码
#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 了,结果还是假了,甚至比暴力分低(
还以为你们能比学长强点
还是算了😅😅
完结撒花~