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一样好的话)

传统题

数学题,这我哪会啊

posted @ 2024-10-14 21:31  CuFeO4  阅读(14)  评论(0编辑  收藏  举报