EDU 127

A

题意:

给定一个字符串仅含\(a,b\)的字符串,问能否用\(aa,aaa,bb,bbb\)拼成

题解:

只要连续的\(a\)\(b\)长度大于等于\(2\),就可以拼成。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=3e5+10,mod=998244353,inf=2e9;
    int n,m;
    char s[N];
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int T;cin>>T;
        while(T--)
        {
            cin>>(s+1);
            int n=strlen(s+1);
            bool flag=1;
            for(int l=1;l<=n;++l)
            {
                int r=l;
                while(r<n&&s[r+1]==s[l]) ++r;
                //cout<<l<<' '<<r<<"!!"<<endl;
                if(l==r) flag=0;
                l=r;
            }
            if(!flag) cout<<"NO\n";
            else cout<<"YES\n";
        }
    }
}
signed main()
{
    red::main();
    return 0;
}
/*
1
1 2 3 4

*/

B

题意:

数轴上有\(n\)个点,坐标分别是\(a_i\),每个点可以向左,向右或不动,能否让所有点变成连续的一段。

题解:

设两点间距离为\(a_{i+1}-a_i\)

\(num_i\)为相邻两个点距离为\(i\)的对数。

分类讨论:

\(1.\)相邻两个点距离超过\(3\),不行。

\(2.\)\(num_3\geq 2\),不行。

\(3.\)\(num_2+num_3\geq 3\),不行。

\(4.\)\(num_2>0\&\&num_3>0\),不行。

其他情况都可以。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=3e5+10,mod=998244353,inf=2e9;
    int n,m;
    int a[N];
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int T;cin>>T;
        while(T--)
        {
            cin>>n;
            int num1=0,num2=0;
            bool flag=0;
            for(int i=1;i<=n;++i)
            {
                cin>>a[i];
                if(i>1)
                {
                    if(a[i]-a[i-1]<=1) continue;
                    if(a[i]-a[i-1]<=2) ++num1;
                    else if(a[i]-a[i-1]<=3) ++num2;
                    else flag=1;
                }
            }
            if(flag||num2>=2||num1+num2>=3||(num1&&num2)) cout<<"NO\n";
            else cout<<"YES\n";
        }
    }
}
signed main()
{
    red::main();
    return 0;
}
/*
5
3
1 3 5
3
1 2 3
4
1 2 3 7
1
1000000
3
2 5 6

*/

C

题意:

\(n\)家商店,第\(i\)家商店以\(a_i\)的价格出售糖果,只卖一次。

每过一天,商店糖果价格涨一块钱。问每天拿\(x\)元,最多买几包糖。

题解:

先按价钱从小到大排序,然后每天二分答案。

但是其实不能一天一天的算。

可以发现有连续的很多天,二分的结果应该是一样的,直接跳过这些天。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=3e5+10,mod=998244353,inf=2e9;
    int n,m,sum;
    int a[N],s[N];
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int T;cin>>T;
        while(T--)
        {
            cin>>n>>m;
            sum=0;
            for(int i=1;i<=n;++i) cin>>a[i];
            sort(a+1,a+n+1);
            for(int i=1;i<=n;++i)
            {
                s[i]=s[i-1]+a[i];
            }
            for(int t=0;;)
            {
                int l=1,r=n;
                while(l<=r)
                {
                    if(s[mid]+mid*t<=m) l=mid+1;
                    else r=mid-1;
                }
                int k=l-1;
                if(!k) break;
                int d=m-(s[k]+k*t);
                d=d/k+1;
                sum+=k*d;
                t+=d;
            }
            cout<<sum<<'\n';
        }
    }
}
signed main()
{
    red::main();
    return 0;
}
/*
1
1 2 3 4

*/

D

题意:

给定一个长度为\(n\)的数列\(a\)

\(1\sim m\)插入到数列中的任意位置。

然后让数列相邻两个位置的数字大小的差值之和最小。

题解:

想象一下,如果有两个数字\(x,y(x<y)\)相邻,那么我就可以把\(x\sim y\)的所有数字插入到他两之间,而不额外产生任何代价。

所以只要判断一下,如果\(a\)中的最大数字没有\(m\)大,那就找个最合适的位置把\(m\)插入进去。

如果\(a\)中的最小数字没有\(1\)小,就再找个合适的位置把\(1\)插入进去。

然后算所有相邻位置的数字大小差值的和就可以了。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=5e5+10,mod=998244353,inf=2e9;
    int n,m;
    int a[N],c[N];
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int T;cin>>T;
        while(T--)
        {
            cin>>n>>m;
            int sum=0;
            int minn=inf,maxn=-inf;
            for(int i=1;i<=n+1;++i) c[i]=0;
            for(int i=1;i<=n;++i)
            {
                cin>>a[i];
                minn=min(minn,a[i]),maxn=max(maxn,a[i]);
                if(i>1)
                {
                    int l=a[i-1],r=a[i];
                    if(l>r) swap(l,r);
                    sum+=r-l;
                    ++c[l],--c[r+1];
                }
            }
            for(int i=1;i<=n;++i)
            {
                c[i]+=c[i-1];
            }
            sum+=minn-1;
            sum+=min(0ll,m-maxn);
            cout<<sum<<'\n';
        }
    }
}
signed main()
{
    red::main();
    return 0;
}
/*
1
1 2 3 4


7 2 2 3 4 5 6 7 8 10

*/

E

题意:

给定一个有\(2^n-1(n\leq 18)\)个节点的完全二叉树。每个顶点上有一个字母\(A\)\(B\)

每个节点可以翻转左右儿子,可以产生多少种先序遍历不同的字符串。

如果左右儿子同构,那么\(dp[x]=dp[son[x][0]]*dp[son[x][1]]\)

否则,\(dp[x]=2*dp[son[x][0]]*dp[son[x][1]]\),因为可以决定谁是左子树。

怎么判断左右子树同构?暴力取字典序最小的情况。

一个节点最多作为\(18\)个节点的子节点,所以\(\sum_{x=1}^{2^n-1}str[x]\sim n*(2^{n-1})\)

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=1e6+10,mod=998244353,inf=2e9;
    int n,m;
    char s[N];
    string str[N];
    int dp[N];
    inline void dfs(int now)
    {
        if(now*2>=(1<<n))
        {
            dp[now]=1;
            str[now]=s[now];
            //cout<<now<<' '<<s[now]<<' '<<dp[now]<<"!!"<<endl;
            return;
        }
        dfs(now<<1);
        dfs(now<<1|1);
        string t1=str[now<<1],t2=str[now<<1|1];
        int f1=dp[now<<1],f2=dp[now<<1|1];
        if(t1==t2) dp[now]=f1*f2%mod;
        else dp[now]=f1*f2%mod*2%mod;
        if(t1<t2) str[now]=t1+s[now]+t2;
        else str[now]=t2+s[now]+t1;
        //cout<<now<<' '<<str[now]<<' '<<dp[now]<<"!!"<<endl;
    }
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);          
        cin>>n;
        cin>>(s+1);
        dfs(1);
        cout<<dp[1]<<'\n';
    }
}
signed main()
{
    red::main();
    return 0;
}
/*
4
BAAAAAAAABBABAB

*/

F

题意:

计算有\(n\)个元素,\(k\)个逆序对,恰好\(x\)个位置满足\(p_i>p_{i+1}\)的排列的个数。

\(n\leq 998244352,1\leq k\leq 11,1\leq x\leq 11\)

题解:

根据\(dwt\)大爷说的,计数题要先划分等价类,对每种等价类单独计算种类数,再把各种等价类拼起来。

发现要求的逆序对数很少,下降对数也很少,说明大部分元素都在自己本来的位置待着,即\(p_i=i\)

相信可以发现(可以个屁),如果存在一个位置,满足\(max\{a_1……a_i\}=i\),那么\(i\)之前的部分和之后的部分将不会产生任何逆序对。

所以我们可以枚举一段,在这一段之中,除了结尾位置,所有位置的前缀最大值都不等于自己。

\(f[s][t1][t2][i]\)表示已选数字集合为\(s\),有\(t1\)个逆序对,\(t2\)个下降对,结尾为\(i\)

这里设置状态结尾为\(i\)是为了加入新数字时好更新逆序数和下降对数。

爆搜之后发现,当\(n>12\)时,\(t1>11\),所以只要\(dp\)\(n\leq 12\)的情况。

最后做一个求和,\(s[n][i][j]\)\(n\)个数字,\(i\)个逆序对,\(j\)个下降对的方案数

然后就可以做一个类似背包的\(dp\)

最坏情况是\(21,21,21\)这样的情况,所以最多有\(22\)个数字。

\(dp[a][b][c][d]\)表示\(a\)段,\(b\)个数字,逆序对数为\(c\),下降对数为\(d\)的方案数。

那么就可以枚举我们的最终序列是怎么得到的。

假设最终序列用了\(a\)段,\(b\)个数字,那么我们要把剩下的\(n-b\)\(p_i=i\)的位置安排进去。

不妨看作一个模型:\(x_1+x_2+…+x_a+x_{a+1}=n-b\),其中\(x_i\geq 0\)

那么这种情况的方案数就是\(\dbinom{n-b+a}{a}\)

这个\(n\)很大,但是这个组合数可以递推得到。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=3e5+10,mod=998244353,inf=2e9;
    int n=12,m;
    int f[(1<<12)+10][12][12][13];
    int s[13][12][12];
    int dp[13][23][12][12];
    int inv[55];
    //已选集合为S,有t1个逆序对,t2个下降对,结尾为i
    inline int fast(int x,int k)
    {
        int ret=1;
        while(k)
        {
            if(k&1) ret=ret*x%mod;
            x=x*x%mod;
            k>>=1;
        }
        return ret;
    }
    inline int all(int n)
    {
        return (1LL<<n)-1;
    }
    inline void add(int &x,int y)
    {
        x=(x+y>=mod)?(x+y-mod):(x+y);
    }
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        inv[0]=inv[1]=1;
        for(int i=2;i<=22;++i) inv[i]=fast(i,mod-2);
        for(n=2;n<=12;++n)
        {
            for(int s=0;s<=all(n);++s)
                for(int j=0;j<=11;++j)
                    for(int k=0;k<=11;++k)
                        for(int l=0;l<=n;++l)
                            f[s][j][k][l]=0;
            f[0][0][0][0]=1;
            for(int o=1;o<=n;++o)
            {
                for(int s=0;s<=all(n);++s)
                for(int c1=0;c1<=11;++c1)
                for(int c2=0;c2<=11;++c2)
                for(int lst=0;lst<=n;++lst)
                if(f[s][c1][c2][lst]&&__builtin_popcount(s)+1==o)
                {
                    int mx=0;
                    for(int i=1;i<=n;++i)
                        if((s>>(i-1))&1) mx=i;
                    for(int j=1;j<=n;++j) if(!((s>>(j-1))&1))
                    {
                        if(max(mx,j)>o||o==n)
                        {
                            int t1=c1+__builtin_popcount(s&(all(n)-all(j)));
                            int t2=c2+(lst>j);
                            if(t1<=11&&t2<=11) add(f[s|(1<<(j-1))][t1][t2][j],f[s][c1][c2][lst]);
                        }
                    }
                }
            }
            for(int i=1;i<=11;++i)
                for(int j=1;j<=11;++j)
                    for(int k=1;k<=n;++k)
                        add(s[n][i][j],f[all(n)][i][j][k]);
        }
        dp[0][0][0][0]=1;
        for(int a=0;a<=11;++a)
            for(int b=0;b<=22;++b)
                for(int c=0;c<=11;++c)
                    for(int d=0;d<=11;++d)
                        if(dp[a][b][c][d])
                            for(n=2;n<=12;++n)
                                for(int i=1;c+i<=11;++i)
                                    for(int j=1;d+j<=11;++j)
                                        add(dp[a+1][b+n][c+i][d+j],dp[a][b][c][d]*s[n][i][j]%mod);
        int T;cin>>T;  
        while(T--)
        {
            int x,y,z;cin>>x>>y>>z;
            int ans=0;
            for(int i=2;i<=min(22ll,x);++i)
            {
                int tmp=x-i+1;
                for(int j=1;j<=11;++j)
                {
                    add(ans,dp[j][i][y][z]*tmp%mod);
                    tmp=tmp*(x-i+j+1)%mod*inv[j+1]%mod;
                    //cout<<(x-i+j+1)<<"!!"<<endl;
                }
                //x-i个数,插入j个段
                //x1+x2+x3+x_{j+1}=x-i+(j+1)
                //C(x-i+(j+1)-1,j)
            }
            cout<<ans<<'\n';
        }
    }
}
signed main()
{
    red::main();
    return 0;
}
/*
1
1 2 3 4

*/
posted @ 2022-05-03 16:42  lovelyred  阅读(25)  评论(0编辑  收藏  举报