CSP-S模拟6

从今往后,教室里再也没有我们的一席之地了**希望我高中毕业之前再也不要回去***

A. 玩水

针对n=2的数据点思考了一下,发现了对角线这个事,于是我就判断的一下能找到两个对角线就好了,但其实它有条件!

因为只能往右下走,不满足以上条件根本就过不去,还有上下相邻的图题解没有画,diy画一下吧。

code


#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1003;

int T, n, m;
vector<pair<int, int> > cnt;
bool f;
char s[maxn][maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}

int main()
{
    freopen("water.in", "r", stdin);
    freopen("water.out", "w", stdout);
    
    T = read();
    while(T--)
    {
        n = read(); m = read();
        f = 0;
        cnt.clear();
        for(int i=1; i<=n; i++)
        {
            scanf("%s", s[i]+1);
        }
        for(int i=2; i<=n; i++)
        {
            if(f) break;
            for(int j=1; j<m; j++)
            {
                if(s[i][j] == s[i-1][j+1]) 
                {
                    cnt.push_back(make_pair(i, j));
                    int sz = cnt.size();
                    for(int k=0; k<sz; k++)
                    {
                        if(cnt[k].first < i && cnt[k].second < j)
                        {
                            f = 1; break;
                        }
                        if(cnt[k].first > i && cnt[k].second > j)
                        {
                            f = 1; break;
                        }
                        if(cnt[k].first == i && cnt[k].second == j-1)
                        {
                            f = 1; break;
                        }
                        if(cnt[k].first == i-1 && cnt[k].second == j)
                        {
                            f = 1; break;
                        }
                    }
                    if(f) break;
                }
            }
        }
        if(f) printf("1\n");
        else printf("0\n");
    }
    
    return 0;
}

 

B. AVL 树

我找到了高度为i的平衡树最少的节点数目方程f[i] = f[i-1] + f[i-2] + 1,我还想到了按照先序遍历的顺序贪心的去找k个点,但是怎么找我就不会了***

判断每个点能不能加入最终的树中的条件就是判断得到这个点整棵树至少多大,是不是超过了k,计算大小时从当前点向上,每次遇到自己是左子树时,根据目前的情况计算右子树至少留下多少点。

如果每一次只判断当前的点就还好,可是以前加入的点还要留下处理起来就很奇妙。

由于加入的点再也不会被删除,我们可以直接修改点数。统计标记的时候可以用^1符号避免重复计算。use保存以i为根的子树中被占用的最大深度,它用来判断大小。左子树大小对右子树的限制用lim记录,这就处理了之前选的点对之后操作造成的影响。

在判断的时候只需要处理不是右子树的情况,因为右子树点总会跳到不是右子树的点,然后再计算以他为根的最小点数,就包括了从右子树上跳上来的点,由于使用了贪心策略,最终选的右子树大小一定会恰好等于所有左子树给这棵右子树设的下限中的最大值。

code


#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 5e5 + 3;

int n, m, root, mxdep[maxn], dep[maxn], use[maxn], lim[maxn], g[40], fa[maxn], son[maxn][2];
bool vis[maxn];
#define ls son[x][0]
#define rs son[x][1]

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}

void dfs(int x)
{
    dep[x] = dep[fa[x]]+1; mxdep[x] = dep[x];
    if(ls) dfs(ls), mxdep[x] = max(mxdep[x], mxdep[ls]);
    if(rs) dfs(rs), mxdep[x] = max(mxdep[x], mxdep[rs]);
}

void insert(int x)
{
    int temp = use[x] = max(use[x], dep[x]);
    while(x)
    {
        m -= vis[x]^1; vis[x] = 1; use[x] = max(use[x], temp);
        if(x == son[fa[x]][0] && son[fa[x]][1] && !vis[son[fa[x]][1]])
        {
            lim[son[fa[x]][1]] = max(lim[son[fa[x]][1]], use[x]-1);
        }
        x = fa[x];
    }
}

bool judge(int x)
{
    int temp = max(dep[x], use[x]), cnt = 0;
    while(x)
    {
        cnt += vis[x]^1; temp = max(temp, use[x]);
        if(x == son[fa[x]][0] && son[fa[x]][1])
        {
            cnt += g[max(temp-1, lim[son[fa[x]][1]])-dep[fa[x]]];
        }
        x = fa[x];
    }
    return cnt <= m;
}

void work(int x)
{
    int tmp1 = ls, tmp2 = rs; 
    if(mxdep[ls] >= lim[x]) swap(tmp1, tmp2);
    lim[tmp1] = max(lim[tmp1], lim[x]-1); lim[tmp2] = max(lim[tmp2], lim[x]);
}

void solve(int x)
{
    if(judge(x)) insert(x);
    if(ls&&rs) work(x);
    else if(ls) lim[ls] = max(lim[ls], lim[x]);
    else if(rs) lim[rs] = max(lim[rs], lim[x]);
    if(ls) solve(ls); if(rs) solve(rs);
}

int main()
{
    freopen("avl.in", "r", stdin);
    freopen("avl.out", "w", stdout);
    
    n = read(); m = read();
    g[1] = 1;
    for(int i=2; i<=30; i++) g[i] = g[i-1] + g[i-2] + 1;
    for(int i=1; i<=n; i++)
    {
        fa[i] = read();
        if(~fa[i] && fa[i]<i) son[fa[i]][1] = i;
        else if(~fa[i] && fa[i]>i) son[fa[i]][0] = i;
        else root = i;
    }
    fa[root] = 0; dfs(root); solve(root);
    for(int i=1; i<=n; i++)
    {
        printf("%d", (int)vis[i]);
    }
    
    return 0;
}

 

C. 暴雨

 继续鹤:f[i][j[0/1][k]表示前列,已经铲平了j块,积水的体积是偶数/奇数,最大值是k。对最大值的表示比较神奇,把土地填平对最大值的影响至多让它变成m小值,所以最后一维记录最大值的相对大小,nex预处理同一个值在转移之后相对大小变成了什么。

一个位置有没有积水要看它左边的最大值和右边的最大值取较小的一个,同时对两个方向没法dp了,可以把它拆开,假设另一个方向是墙,把两个方向分开。由于这些土地中一定有最高的一个或几个(哦对记录答案时还要注意取等),可以枚举最高的土地的位置,在这个位置把左右合并。

开ll居然会MLE,看来能开ll的就开上的方法并不是非常保险***

code


#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 25003;
const int mod = 1e9 + 7;

int n, m, b1[maxn][27], b2[maxn][27], cnt1[maxn], cnt2[maxn], a[maxn];
int nex1[maxn][27], nex2[maxn][27], new1[maxn], new2[maxn];
int f1[maxn][27][2][27], f2[maxn][27][2][27], ans;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}

inline void add(int &x, int y) {x = (x+y)%mod;}
multiset<int> s;
inline int find1(int i, int x)
{
    return lower_bound(b1[i], b1[i]+cnt1[i]+1, x)-b1[i];
}
inline int find2(int i, int x)
{
    return lower_bound(b2[i], b2[i]+cnt2[i]+1, x)-b2[i];
}

int main()
{
    freopen("rain.in", "r", stdin);
    freopen("rain.out", "w", stdout);
    
    n = read(); m = read();
    for(int i=1; i<=n; i++) a[i] = read();
    for(int i=1; i<=n; i++)
    {
        s.insert(a[i]);
        if(s.size()>m+1) s.erase(s.begin());
        for(auto it : s) b1[i][++cnt1[i]] = it;//从左到右截止到i最大的m+1个数
    }
    s.clear();
    for(int i=n; i>=1; i--)
    {
        s.insert(a[i]);
        if(s.size()>m+1) s.erase(s.begin());
        for(auto it : s) b2[i][++cnt2[i]] = it;
    }
    for(int i=0; i<n; i++)
    {
        for(int j=0; j<=cnt1[i]; j++)
        {
            nex1[i][j] = find1(i+1, b1[i][j]);
        }
    }
    for(int i=n+1; i>1; i--)
    {
        for(int j=0; j<=cnt2[i]; j++)
        {
            nex2[i][j] = find2(i-1, b2[i][j]);
        }
    }
    for(int i=1; i<=n; i++) new1[i] = find1(i, a[i]);
    for(int i=n; i>=1; i--) new2[i] = find2(i, a[i]);
    f1[0][0][0][0] = 1;
    for(int i=0; i<n; i++)
    {
        for(int j=0; j<=m; j++)
        {
            for(int k=0; k<=cnt1[i]; k++)
            {
                if(f1[i][j][0][k])
                {
                    if(a[i+1]<b1[i][k]) add(f1[i+1][j][(b1[i][k]-a[i+1])&1][nex1[i][k]], f1[i][j][0][k]);
                    else add(f1[i+1][j][0][new1[i+1]], f1[i][j][0][k]);
                    if(j<m) add(f1[i+1][j+1][b1[i][k]&1][nex1[i][k]], f1[i][j][0][k]);
                }
                if(f1[i][j][1][k])
                {
                    if(a[i+1]<b1[i][k]) add(f1[i+1][j][(b1[i][k]-a[i+1])&1^1][nex1[i][k]], f1[i][j][1][k]);
                    else add(f1[i+1][j][1][new1[i+1]], f1[i][j][1][k]);
                    if(j<m) add(f1[i+1][j+1][b1[i][k]&1^1][nex1[i][k]], f1[i][j][1][k]);
                }
            }
        }
    }
    f2[n+1][0][0][0] = 1;
    for(int i=n+1; i>1; i--)
    {
        for(int j=0; j<=m; j++)
        {
            for(int k=0; k<=cnt2[i]; k++)
            {
                if(f2[i][j][0][k])
                {
                    if(a[i-1] < b2[i][k]) add(f2[i-1][j][(b2[i][k]-a[i-1])&1][nex2[i][k]], f2[i][j][0][k]);
                    else add(f2[i-1][j][0][new2[i-1]], f2[i][j][0][k]);
                    if(j<m) add(f2[i-1][j+1][b2[i][k]&1][nex2[i][k]], f2[i][j][0][k]);
                }
                if(f2[i][j][1][k])
                {
                    if(a[i-1] < b2[i][k]) add(f2[i-1][j][(b2[i][k]-a[i-1])&1^1][nex2[i][k]], f2[i][j][1][k]);
                    else add(f2[i-1][j][1][new2[i-1]], f2[i][j][1][k]);
                    if(j<m) add(f2[i-1][j+1][b2[i][k]&1^1][nex2[i][k]], f2[i][j][1][k]);
                }
            }
        }
    }
    for(int i=1; i<=n; i++)
    {
        for(int j=0; j<=m; j++)
        {
            for(int k1=0; k1<=cnt1[i-1]&&b1[i-1][k1]<a[i]; k1++)
            {
                if(f1[i-1][j][0][k1])
                {
                    for(int k2=0; k2<=cnt2[i+1]&&b2[i+1][k2]<=a[i]; k2++)
                    {
                        if(f2[i+1][m-j][0][k2])
                        {
                            ans = (ans+1ll*f1[i-1][j][0][k1]*f2[i+1][m-j][0][k2]%mod)%mod;
                        }
                    }
                }
                if(f1[i-1][j][1][k1])
                {
                    for(int k2=0; k2<=cnt2[i+1]&&b2[i+1][k2]<=a[i]; k2++)
                    {
                        if(f2[i+1][m-j][1][k2])
                        {
                            ans = (ans+1ll*f1[i-1][j][1][k1]*f2[i+1][m-j][1][k2]%mod)%mod;
                        }
                    }
                }
            }
        }
    }
    printf("%d\n", ans);
    
    return 0;
}

 

D. 置换

来日方长……

posted @ 2022-09-19 21:53  Catherine_leah  阅读(11)  评论(0编辑  收藏  举报
/* */