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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】