每一年都奔走在自己热爱里

没有人是一座孤岛,总有谁爱着你

Codeforces Round #848 (Div. 2)(B,C,D)

Codeforces Round #848 (Div. 2)(B,C,D)

哎呀,最近不知道怎么回事,题目老是没看懂,还是心不在此,太烦了,好几天都只是一道题了

B

B

这道题到看答案才发现我理解错了,我怎么说越想越复杂,我就说第二道题怎么会这么复杂

其实题目的意思是要求我们每一个都满足条件才是nogood,我以为是只有一个就是nogood,看得不仔细

现在我看完了题解,发现我的题都读错了,真离谱

这道题大意是

给你一个长度为n的排序,还有一个长度为m的数组,还有一个d,我们对于这个数组是nogood有以下的条件

即 对于1m1都需要满足pos(ai)<pos(ai+1)<=pos(ai)+d

我们要求的是要这个数组变成good,的最小移动数量

移动是什么?

移动可以改变排序p里面相邻的两个数(注意是改变p里面的数的位置,而不是a里面的数的位置)

所以我们只有一个不满足以上nogood的条件,那么这个就是good的,那么我们不需要移动,所以答案就是0

如果每一个都满足nogood的条件,那么我们要怎么让这一个数不满足nogood的条件

对于此时pos(ai)<pos(ai+1)<=pos(ai)+d,我们要让这一对不满足这个条件,有两种移动方法

第一种,让pos(ai)>pos(ai+1),此时,我们需要移动的是把aiai+1的位置转换一下

第二种,是让pos(ai+1)>pos(ai)+d,我们要让这两个数的位置差大于d,我们还要考虑我们可不可以存在这两个数之间的距离大于d,我们知道对于长度为n的排序,两个数之间的最大差为n1,所以,要想进行第二种移动,我们要满足n1>d

然后我们取较小的那一个移动次数

#include <iostream>
#include <string>
using namespace std;
const int maxn=1e5+10;
int t,n,d,m;
int pos[maxn],k;
int a[maxn];
void solve()
{
    cin>>n>>m>>d;
    for (int i=1;i<=n;i++)
    {
        int x;
        cin>>x;
        pos[x]=i;
    }
    int ans=1e9;
    for (int i=1;i<=m;i++)
    {
        cin>>a[i];
    }
    for (int i=1;i<m;i++)
    {
        if (pos[a[i]]<pos[a[i+1]]&&pos[a[i+1]]<=pos[a[i]]+d)
        {
             int c1=pos[a[i+1]]-pos[a[i]];//交换两个位置,让pos[a[i]]>pos[a[i+1]]
             int c2=1e9;
             if (d<n-1)//n-1是这两个数的最远距离,n-1,只有两点距离大于d才可以满足这第二个情况,pos[a[i+1]]>pos[a[i]]+d,那么这个d只能小于n-1才可
             {
                c2=pos[a[i]]+d+1-pos[a[i+1]];
             }
            int c=min(c1,c2);
            ans=min(ans,c);
        }
        else 
        {
            cout<<0<<'\n';
            return ;
        }
    }
    cout<<ans<<'\n';
    return ;
}
signed main ()
{
    cin>>t;
    while (t--)
    {
        solve();
    }
    system ("pause");
    return 0;
}

C

C

这个题大意是给你两个字符串ab,我们需要尽量的让l,r的对数尽量多

l,r需要满足alr这一个区间的字符和b同样位置的字符一样的

我们可以实行任意次操作

apos位置的x变成y,把x加进去一个集合,我们要保证这个集合里面的字符最多有k种,也就说说对于每次改变,我们一定要改变k个位置pos,但是我们要保证这k个位置的字符的种类不可以大于k

对于这个限制,我们首先可以把所有需要改变的字符ai不等于biai的种类记下来,cnt记录下来

然后我们可以看到题目中那个加粗字,ab的不同字符种类不会超过10个,那么对于这些对于我们可以取的数量min(cnt,k),我们可以用二进制的形式把状态压缩,列举每一个状态

每一个状态都代表着其中的一类字符的改变还是保留的情况

对于这一类的字符,如果需要是改变成b里面的那一个字符,那么所有需要改变的都要改变,只有这样,才可以让l,r的数量最大

那么怎么求l,r的数量

对于如果有一段长度为tot的字符和b是一样的,那么我们可以有tot(tot+1)/2种不同的l,r,这里面的不同l,r,里面的l都是这一段字符的第一个字符的位置,r就是后面的l,l+1,l+2,...,l+tot1,有tot个,对于l取这一段字符的第二个位置,r取值可为l+1,l+2,...l+tot1,有cnt1个,由此可以得出

对于不同的l,有tot,tot1,tot2...1个,那么这个数量和我们可以用前缀和求

然后对于不同的状态,我们选取那个可以得到最大的对数的那一个状态

#include <iostream>
#include <string>
#include <vector>
using namespace std;
#define int long long 
int t,n,k;
string a,b;
void solve()
{
    cin>>n>>k;
    cin>>a>>b;
    vector<int>vis(30,0);
    vector<int>code(30,0);
    int cnt=0;
    for (int i=0;i<n;i++)
    {
        vis[a[i]-'a']=1;
    }
    for (int i=0;i<26;i++)
    {
        if (vis[i])
        {
            code[cnt++]=i;
        }
    }
    k=min(k,cnt);
    int M=1<<cnt;
    int ans=0;
    for (int i=0;i<M;i++)
    {
        vector<bool>use(26,false);
        int tot=0;
        for (int j=0;j<cnt;j++)
        {
            if ((i>>j)&1)
            {
                use[code[j]]=true;
                tot++;
            }
        }
        if (tot==k)
        {
            string tmp=a;
            for (int j=0;j<n;j++)
            {
                if (use[tmp[j]-'a'])
                {
                    tmp[j]=b[j];
                }
            }
            int tot=0;
            int sum=0;
            for (int j=0;j<n;j++)
            {
                if (tmp[j]==b[j])
                {
                    tot++;
                }
                else 
                {
                    sum+=tot*(tot+1ll)/2;
                    tot=0;
                }
                if (j==n-1)
                {
                    sum+=tot*(tot+1ll)/2;
                }
            }
            ans=max(ans,sum);
        }
    }
    cout<<ans<<'\n';
    return ;
}
signed main ()
{
    cin>>t;
    while (t--)
    {
        solve();
    }
    system ("pause");
    return 0;
}

D

D

这个题大意是我们有两种01字符串,我们最后要把a字符变成b字符,对于每一个位置的,都有平等的概率可以把a里面的这一个位置的字符翻转,0变成1,1变成0

问最后把a变成b的概率

我们可以定义f[i]为此时由i1个相同的位置,变成i个相同的位置的概率

状态转移是怎么转移的

f[i+1]=nin+in×(1+f[i]+f[i+1])

这个是怎么来的呢

对于此时已经有i个相同的位置,

如果这一次我们直接选择那些不相同的位置进行翻转,那么已经有i+1个相同的位置

如果这一次我们选择那些相同的位置的字符翻转,那么此时相同的位置就变成了了i1个,那么我们要把i1变成i个,这一步的概率应该为f[i],然后再又要把此时已经有的i个相同位置变成i+1个相同位置,这一步的概率为f[i+1]

但是我们最好是把未知数放在同一边,所以我们可以在纸上进行变换

可以得到以下这个方程

f[i+1]=(1+ini)×(f[i]+1)

然后我们从原来有cnt个相同的变成n个相同的,需要在这一系列的变化的概率相加

#include <iostream>
#include <string>
using namespace std;
#define int long long 
const int mod=998244353;
const int maxn=1e6+10;
int t,n;
string a,b;
int f[maxn];
int ksm(int x,int p)
{
    int res=1;
    while (p)
    {
        if (p&1)
        {
            res=(res*x)%mod;
        }
        x=(x*x)%mod;
        p>>=1;
    }
    return res%mod;
}
int inv(int x)
{
    return ksm(x,mod-2);
}
void solve()
{
    cin>>n;
    cin>>a>>b;
    int cnt=0;
    for (int i=0;i<n;i++)
    {
        if (a[i]==b[i])
        {
            cnt++;
        }
    }
    f[1]=1;
    for (int i=1;i<n;i++)
    {
        f[i+1]=(1+i*inv(n-i)%mod*(f[i]+1))%mod;
    }
    int ans=0;
    for (int i=cnt+1;i<=n;i++)
    {
        ans=(ans+f[i])%mod;
    }
    ans%=mod;
    cout<<ans<<'\n';
    return ;
}
signed main ()
{
    cin>>t;
    while (t--)
    {
        solve();
    }
    system ("pause");
    return 0;
}
posted @   righting  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示