牛客周赛 Round 31

嗯嗯摆烂了

前三道签到题就不说了,剩下的题都挺有教育意义的

题目D:小红数组操作——>有两个操作:序号1是输入x,y将x插入在y的右边。保证此时数组没有元素等于x,且数组中存在一个y,当y等于0则把x插在最左边。序号2是输入x,将元素x删除

思路:刚一拿到这一题你会想到是链表,但是链表是根据idx(第几个插入的数进行添加删除的)但这里是具体的数值,我们可以引入一个mapkey是原本的数,val是它对应idx的值这样一来我们就可以通过map来找要在那个位置插入了,单双链表都可以,赛时在acwing 上的双,但单也可以实现,对于删除一个值 我们不把它真正意义上的删去而是直接改变它的e值相当于做一个标记就好

单链表 

#include
using namespace std;
mapmp;
const int N = 2e5+10;
int e[N],ne[N],idx=1,head=-1;

void add(int k,int x)
{
    if(k)
    {
        mp[x]=idx;
        e[idx]=x,ne[idx]=ne[k],ne[k]=idx++;
    }
    else
    {
        mp[x]=idx;
        e[idx]=x,ne[idx]=head,head=idx++;
    }
}

int main()
{
    int q;cin>>q;
    int n=0;
    while(q--)
    {
        int op,x,y;
        cin>>op;
        if(op==1)
        {
            n++;
            cin>>x>>y;
            add(mp[y],x);     
        }
        else
        {
            n--;
            cin>>x;
            e[mp[x]]=-1;
        }
    }
    cout<

双链表

#include
using namespace std;
mapmp;
const int N = 2e5+10;
int e[N],l[N],r[N],idx;

void init()
{
    //0为左端点,1为右端点;
    r[0]=1,l[1]=0;
    idx=2;
}

void add(int k,int x)
{
    mp[x]=idx;
    e[idx]=x;
    l[idx]=k;
    r[idx]=r[k];
    l[r[k]]=idx;//18与19句不能写反
    r[k]=idx;
    idx++;
}

void del(int k)
{
    r[l[k]]=r[k];
    l[r[k]]=l[k];
}

int main()
{
    init();
    int q;cin>>q;
    int cnt1=0,cnt2=0;
    while(q--)
    {
        int op,x,y;
        cin>>op;
        if(op==1)
        {
            cnt1++;
            cin>>x>>y;
            if(y==0)add(0,x);
            else
            {
                add(mp[y],x); 
            }
        }
        else
        {
            cnt2++;
            cin>>x;
            //del(mp[x]);
            e[mp[x]]=-1;
        }
    }
    cout<

 题目E:小红的子集取反(变种01背包)——>小红得到一个数组,她可以对其中的一些元素乘-1。要想使得最后元素和为0,问最少的操作次数,没有则输入-1

思路:首先它是一道dp题(咱就是説一点没看出来,光贪了服。dp[i][j]表示前i个数,使得最终元素和为j的数量的最小值

状态计算1:当前的数不乘-1-->dp[i][j]=min(dp[i][j],dp[i-1][j+x];

                    当前的数乘-1-->dp[i][j]=min(dp[i][j],dp[i-1][j-x]+1);加上一次操作次数

由于每一个x是-200~200的范围,他们最小达到了-40000,数组下标会越界,我们给二维统一加上一个偏移量

#include//01背包
using namespace std;

int dp[210][80080];//第二维统一加了一个偏移量
//dp[i][j]:表示前i个数,使得最终元素和为j的数量的最小值
int main()
{
    memset(dp,0x3f,sizeof dp);
    dp[0][4000]=0;
    int q;cin>>q;
    for(int i=1;i<=q;i++)
    {
        int x;cin>>x;
        for(int j=0;j<=8000;j++)
        {
            if(j+x>=0&&j+x<=8000)dp[i][j]=min(dp[i][j],dp[i-1][j+x]);
            if(j-x>=0&&j-x<=8000)dp[i][j]=min(dp[i][j],dp[i-1][j-x]+1);//改变符号
        }
    }
    
    if(dp[q][4000]>0x3f3f3f3f/2)cout<<-1<

 题目F:小红的连续段(组合数学)——>她定义了连续段的数量为相同字符的极长连续子串的数量如“aabbaaa"能分成三段"aa","bb","aaa",现在她让你求出长度为x+y包含x个'a'和y个'b'组成的字符串连续段数量恰好为i的字符串数量。输入[1,x+y]中的第i个答案

思路:这是一个经典的“隔板法”模型,我们首先讨论形如“ababa..”类型的,那么a有i/2段,b有i/2段,这个可以形象的用这种模型:一串“aaa.."切cnt刀,的方案数,求组合数用到了快速幂求逆元(x,mod-2),和组合公式:C[n][m]=!n/(!m*!(n-m))

//隔板法模型
#include

using namespace std;

typedef long long LL;

const int MOD=1e9+7;

LL jc[2010];

long long qmi(LL a,LL b)
{
    LL res=1;
    while(b)
    {
        if(b&1)res=res*a%MOD;
        b>>=1;
        a=a*a%MOD;
    }
    return res;
}

long long inv(int x)
{
    return qmi(x,MOD-2);
}

long long C(int n,int m)
{
    if(m<0||n-m<0)return 0;//除去不合法方案
    return jc[n]*inv(jc[m])%MOD*inv(jc[n-m])%MOD;
}

int main()
{
    jc[0]=1;
    for(int i=1;i<=2010;i++)
        jc[i]=jc[i-1]*i%MOD;
    
    int x,y;
    cin>>x>>y;
    for(int i=1;i<=x+y;i++)
    {
        int ji=i/2+i%2,ou=i/2;
        //x个a,x-1个空隙,插的板子是要求的分隔数少1
        cout<<(C(x-1,ji-1)*C(y-1,ou-1)+C(y-1,ji-1)*C(x-1,ou-1))%MOD<

你可能感兴趣的:(算法)