csp-s模拟11
赛时rank 11,T1 100pts,T2 17pts,T3 0pts,T4 0pts,T5 10pts
这场模拟赛就是糖,告诉我题目难度不按升序排列就是除了T1我都不会呗。
玩水 (water)
签成了,还签了个首切?
定义一个形如\(\begin{matrix} A*\\*\ \end{matrix}\)的为一个角,角的位置为A的位置。
有解的时候就是两个角相邻或者一个角在另一个角的左上方。
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define InF(x) freopen(x".in","r",stdin)
#define OutF(x) freopen(x".out","w",stdout)
#define ErrF(x) freopen(x".err","w",stderr)
#define AnsF(x) freopen(x".ans","w",stdout)
#define rep(i,s,t,p) for(int i = s;i <= t; i += p)
#define drep(i,s,t,p) for(int i = s;i >= t; i -= p)
#ifdef LOCAL
FILE *InFile = InF("in"),*OutFile = OutF("out");
// FILE *ErrFile = ErrF("err");
#else
FILE *Infile = InF("water"),*OutFile = OutF("water");
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 1e3 + 10;
int n,m;
int flag[N][N],pd[N][N];
char a[N][N];
inline int get(int x1,int y1,int x2,int y2){
return flag[x2][y2] - flag[x2][y1-1] - flag[x1-1][y2] + flag[x1-1][y1-1];
}
inline void solve(){
cin>>n>>m;
rep(i,0,n+1,1) rep(j,0,m+1,1) a[i][j] = ' ';
rep(i,0,n+1,1) rep(j,0,m+1,1) flag[i][j] = pd[i][j] = 0;
rep(i,1,n,1) cin>>(a[i]+1);
rep(i,1,n,1) rep(j,1,m,1){
if(a[i+1][j] == a[i][j+1]) flag[i][j] = pd[i][j] = 1;
}
rep(i,1,n,1) rep(j,1,m,1){
flag[i][j] += flag[i-1][j] + flag[i][j-1] - flag[i-1][j-1];
}
rep(i,1,n,1) rep(j,1,m,1)
if(pd[i][j] && (flag[i-1][j-1] >= 1||pd[i-1][j]||pd[i][j-1])){
return cout<<"1\n",void();
}
cout<<"0\n";
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
int T;cin>>T;while(T--) solve();
}
AVL 树
唐氏贪心构造题。
先放一个显然的结论:高度为\(i\)的平衡树最少的节点的递推式为\(f_{i} = f_{i-1}+f_{i-2}+1,f_{1} = 1,f_{0} = 0\)
这个可以通过打表或手膜得出来,证明的话手膜一下就明白了。
如果你不愿意手膜怎么办
证明 :
思考一下就会发现,其实就是一个根节点(+1),左边挂一个深度为\(i-1\)的AVL(\(f_{i-1}\)),右边挂一个深度为\(i-2\)的AVL(\(f_{i-2}\))。
所以节点数就是\(f_{i} = f_{i-1}+f_{i-1}+1\)
发现无论怎样,尽可能保留左儿子一定更优,所以优先递归左儿子。
考虑对于每一个节点,如何判断它是否可以保留。
回溯它的所有父亲,对于该节点是左孩子的情况,我们通过深度与AVL的定义,计算出保留它,至少需要右子树中有几个点,直到根,我们便算出了保留这个点需要整棵树至少多大。如果当前剩下的k足够,那就可以选。
但是此时可能会有一个错误,就是左儿子选了太多,右儿子不够选了怎么办?
预估一下AVL的深度。
定义\(dis_x\):以\(x\)为根的子树的极限可能深度;\(nw_x\):已选子树中的最大深度;mx\(x\):如果留下rt,需要至少往下延伸到第几层。
在查询时,利用这些数组找到可能的最大深度,进而判定是否留下;当确定一个点被选时,向上更新它的父亲的最大深度信息。
时间复杂度\(O(n\log n)\)
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define InF(x) freopen(x".in","r",stdin)
#define OutF(x) freopen(x".out","w",stdout)
#define ErrF(x) freopen(x".err","w",stderr)
#define AnsF(x) freopen(x".ans","w",stdout)
#define rep(i,s,t,p) for(int i = s;i <= t; i += p)
#define drep(i,s,t,p) for(int i = s;i >= t; i -= p)
#ifdef LOCAL
FILE *InFile = InF("in"),*OutFile = OutF("out");
// FILE *ErrFile = ErrF("err");
#else
FILE *Infile = InF("avl"),*OutFile = OutF("avl");
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
#define int long long
const int N = 5e5 + 10;
int n,k,rt,son[N][2],dep[N],fa[N],dist[N],nw[N],mx[N],f[N];
bitset<N> ok;
#define eb emplace_back
#define lc(x) son[x][0]
#define rc(x) son[x][1]
void dfs1(int x){
dist[x] = dep[x] = dep[fa[x]] + 1;
if(lc(x)) dfs1(lc(x)),dist[x] = max(dist[x],dist[lc(x)]);
if(rc(x)) dfs1(rc(x)),dist[x] = max(dist[x],dist[rc(x)]);
}
inline void update(int x){
nw[x] = max(nw[x],dep[x]);
int now = x,f = fa[x];
while(f){
nw[f] = max(nw[f],dep[x]);//已经选择的子树中的最大深度
if(now == lc(f) && rc(f)) mx[rc(f)] = max(mx[rc(f)],nw[f] - 1);//至少要多少
now = f,f = fa[f];
}
}
inline int query(int x){
int now = x,F = fa[x],res = 0;
while(F){
if(now == lc(F)) res += f[max({nw[F]-1,dep[x]-1,mx[rc(F)]})-dep[F]];//取出最少深度
now = F,F = fa[F];
}
return res;
}
void dfs2(int x){
if(!x) return;
if(query(x) <= k-1){
ok.set(x);
k--;
update(x);
}
if(lc(x) && dist[lc(x)] >= mx[x]){//左子树优先
mx[lc(x)] = max(mx[lc(x)],mx[x]);
if(rc(x)) mx[rc(x)] = max(mx[rc(x)],mx[x]-1);
}
else if(rc(x)){//如果左子树不够就换右子树
mx[rc(x)] = max(mx[rc(x)],mx[x]);
if(lc(x)) mx[lc(x)] = max(mx[lc(x)],mx[x]-1);
}
dfs2(lc(x));dfs2(rc(x));
}
inline void solve(){
cin>>n>>k;
rep(i,1,n,1){
cin>>fa[i];int f = fa[i];
if(f == -1) rt = i,fa[i] = 0;
else{
if(i < f) lc(f) = i;
else rc(f) = i;
}
}
f[1] = 1;
rep(i,2,30,1) f[i] = f[i - 1] + f[i - 2] + 1;
dfs1(rt);dfs2(rt);
rep(i,1,n,1) cout<<ok[i];
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
solve();
}
暴雨
\(\checkmark\)💩题面,一个积水说的那么复杂。
神仙dp设计,实现思路来自HDK。
记\(f_{i,j,k,0/1}\)表示前\(i\)个中推平了\(k\)个,土块中最大的高度为\(j\),且强制后面存在一个\(\ge j\)的土块,当前积水的体积为奇数/偶数的方案数。
发现第二维只需要开\(k\)个即可,因为只有最大的\(k+1\)个值会产生贡献。
然后前后各跑一遍,乘法原理记录贡献即可。
用unordered_map会被卡,用map有点悬,用cc_hash_table或者gp_hash_table比较好。(本题cc跑的快一点,50个点大概快了一秒)
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(#x".in","r",stdin)
#define outfile(x) freopen(#x".out","w",stdout)
#define errfile(x) freopen(#x".err","w",stderr)
#define ansfile(x) freopen(#x".ans","w",stdout)
#define rep(i,s,t,p) for(int i = s;i <= t; i += p)
#define drep(i,s,t,p) for(int i = s;i >= t; i -= p)
#ifdef LOCAL
FILE *InFile = infile(in),*OutFile = outfile(out);
// FILE *ErrFile = errfile(err);
#else
FILE *Infile = infile(rain),*OutFile = outfile(rain);
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 25010,K = 27,mod = 1e9 + 7;
int n,k,ans,h[N],id[N];
struct DP{
int a[N],f[N][K][K][2],ct[N],val[N][K];//val_{i,j}表示前i个数中第j大的值,ct_{i}记录前i个数有几个前k大值。
set<int> st;//用于记录前k大值
cc_hash_table<int,int> mp[N];//查询某个值在val中对应的排名
#define ins insert
inline void init(){
st.ins(0);
rep(i,1,n,1){
st.ins(a[i]);
auto it = st.rbegin();
rep(j,1,k+1,1){
if(it == st.rend()) break;
mp[i][*it] = ++ct[i];
val[i][ct[i]] = *it;
it++;
}
}
f[0][1][0][0] = ct[0] = 1;val[0][1] = 0;
rep(i,0,n-1,1) rep(j,1,ct[i],1) rep(l,0,k,1) rep(u,0,1,1){
if(f[i][j][l][u])
if(val[i][j] >= a[i + 1]){
//新加入的土块更低,贡献加上两者高度差
f[i+1][mp[i+1][val[i][j]]][l][(u+val[i][j]-a[i+1])%2]
= (f[i+1][mp[i+1][val[i][j]]][l][(u+val[i][j]-a[i+1])%2] + f[i][j][l][u]) % mod;
if(l + 1 <= k) //推平 i+1,加上 [0,val[i][j]] 之间的贡献
f[i+1][mp[i+1][val[i][j]]][l+1][(u+val[i][j])%2]
= (f[i+1][mp[i+1][val[i][j]]][l+1][(u+val[i][j])%2]+f[i][j][l][u])%mod;
}
else{
//反之,答案就是新增的高度,不做出贡献
f[i+1][mp[i+1][a[i+1]]][l][u]
= (f[i+1][mp[i+1][a[i+1]]][l][u] + f[i][j][l][u])%mod;
if(l + 1 <= k) //推平 i+1,加上 [0,val[i][j]] 之间的贡献
f[i+1][mp[i+1][val[i][j]]][l+1][(u+val[i][j])%2]
= (f[i+1][mp[i+1][val[i][j]]][l+1][(u+val[i][j])%2]+f[i][j][l][u])%mod;
}
}
}
};
inline void solve(){
cin>>n>>k;
DP dp1,dp2;
rep(i,1,n,1){
cin>>h[i];
dp1.a[i] = dp2.a[n-i+1] = h[i];
id[i] = i;
}
dp1.init();dp2.init();
sort(id+1,id+1+n,[&](int x,int y){return h[x] > h[y];});
rep(i,1,n,1){
if(h[id[i]] < h[id[k+1]]) break;
drep(j,dp1.ct[id[i]-1],1,1){
if(dp1.val[id[i]-1][j] > h[id[i]]) break;
drep(l,dp2.ct[n-id[i]],1,1){
if(dp2.val[n-id[i]][l] >= h[id[i]]) break;
rep(u,0,k,1){//前后算,乘法原理统计贡献。
ans = (ans + 1ll*dp1.f[id[i]-1][j][u][0]*dp2.f[n-id[i]][l][k-u][0]%mod)%mod;
ans = (ans + 1ll*dp1.f[id[i]-1][j][u][1]*dp2.f[n-id[i]][l][k-u][1]%mod)%mod;
}
}
}
}
cout<<ans<<'\n';
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
solve();
}
置换
不会,可以看Master的题解(如果您的抽象能力和Master一样好的话)
传统题
数学题,这我哪会啊
本文来自博客园,作者:CuFeO4,转载请注明原文链接:https://www.cnblogs.com/hzoi-Cu/p/18466193