[7.7] 纪中C组

时间:2022-06-23 09:49:05

前天有点问题没发
耽搁了
第一题
感觉一点都不水
要把小船的左坐标和右坐标记录下来,在小船左边就移动左坐标啥的

#include <iostream>
#include <cstdio>
using namespace std;
int n,a[10000001],i,j,f[1000001][4];
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d",&n);
    for (i=1;i<=n;i++)
    scanf("%d",&a[i]);
    f[1][0]=a[1];
    for (i=2;i<=n;i++)
    for (j=0;j<=3;j++)
    {
        if (j==0)
        {
            f[i][j]=max(f[i-1][1]+a[i],f[i-1][3]+a[i]);
        }
        else
        if (j==1)
        {
            f[i][j]=max(f[i-1][0]-a[i],f[i-1][2]-a[i]);
        }
        else
        if (j==2)
        f[i][j]=max(f[i-1][0],f[i-1][2]);
        else
        f[i][j]=max(f[i-1][1],f[i-1][3]);
    }
    printf("%d",max(max(max(f[n][0],f[n][1]),f[n][2]),f[n][3]));
}

第二题
没做出来
太长了,没时间补
但是思路还是知道的,先分解质因数,多的数量减去少的,最后快速幂,高精度乘法还有压位

第三题
比较难,跟中位数有关
因为要求1~n每个前缀的中位数(因为后文要求在这些中位数中找最小的中位数,因此这个需要每个前缀中最大的中位数),所以想用快排的就算了吧
我们发现,如果小根堆和大根堆遵守了规则的话,小根堆的对顶就是最大的中位数
所以维护好,添加好就OK了

#include <iostream>
#include <cstdio>
using namespace std;
int n,i,a[1000001],b[1000001],s[1000001],o[1000001],ia,ib,t;
void aup(int d)
{
    while (d>1&&a[d]<a[d/2])
    {
        t=a[d];
        a[d]=a[d/2];
        a[d/2]=t;
        d=d/2;
    }
}
void ado(int d)
{
    int x;
    while (d*2<=ia&&a[d*2]<a[d]||a[d*2+1]<a[d]&&d*2+1<=ia)
    {
        x=d*2;
        if (a[x]>a[x+1]&&x+1<=ia) x+=1;
        t=a[x];a[x]=a[d];a[d]=t;
        d=x;
    }
}
void bdo(int d)
{
    int x;
    while (d*2<=ib&&b[d*2]>b[d]||b[d*2+1]>b[d]&&d*2+1<=ib)
    {
        x=d*2;
        if (b[x]<b[x+1]&&x+1<=ib) x+=1;
        t=b[x];b[x]=b[d];b[d]=t;
        d=x;
    }
}
void bup(int d)
{
    while (d>1&&b[d]>b[d/2])
    {
        t=b[d];
        b[d]=b[d/2];
        b[d/2]=t;
        d=d/2;
    }
}
void qs(int l,int h)
{
    int i=l,j=h,mid=o[(l+h)/2],t;
    if (l>=h) return;
    do
    {
        while (mid>o[i]) i++;
        while (mid<o[j]) j--;
        if (i<=j)
        {
            t=o[i];
            o[i]=o[j];
            o[j]=t;
            i++;j--;
        }
    }
    while (i<=j);
    qs(i,h);
    qs(l,j);
}
int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    scanf("%d",&n);
    for (i=1;i<=n;i++)
    {
        scanf("%d",&s[i]);
        if (i%2==1)
        {
            ia++;
            a[ia]=s[i];
            aup(ia);
            if (a[1]<b[1])
            {
                t=a[1];a[1]=b[1];b[1]=t;
                ado(1);
                bdo(1);
            }
            o[i]=a[1];
        }
        else
        {
            ib++;
            b[ib]=s[i];
            bup(ib);
            if (b[1]>a[1])
            {
                t=b[1];b[1]=a[1];a[1]=t;
                ado(1);
                bdo(1);
            }
            o[i]=a[1];
        }
    }
    qs(1,n);
    printf("%d",o[n/2]);
}

第四题
另类的前缀和
把0看为-1,1看为1
这样,相加的和如果在后面再次找到,就证明这个区间内的01数量是相等的
因此只要每次存和,m1[和]就是右坐标,m2[和]就是左坐标,最后一个for过去找最多就可以了

#include <iostream>
#include <cstring>
#include <memory.h>
#include <cstdio>
using namespace std;
char s[1000001];
int m1[1000001],m2[1000001],i,j,m,ma,mi=2147483647,l;
int main()
{
    freopen("string.in","r",stdin);
    freopen("string.out","w",stdout);
    scanf("%s",&s);
    m=500000;
    memset(m2,21474836,sizeof(m2));
    m2[500000]=0;
    l=strlen(s);
    for (i=1;i<=l;i++)
    if (s[i-1]=='1')
    {
        m++;
        if (m>ma) ma=m;
        if (m<mi) mi=m;
        if (i>m1[m]) m1[m]=i;
        if (i<m2[m]) m2[m]=i;
    }
    else
    {
        m--;
        if (m>ma) ma=m;
        if (m<mi) mi=m;
        if (i>m1[m]) m1[m]=i;
        if (i<m2[m]) m2[m]=i;
    }
    m=0;
    for (i=mi;i<=ma;i++)
    m=max(m1[i]-m2[i],m);
    printf("%d",m);
}