2022牛客OI赛前集训营-提高组(第一场) 赛后总结

https://ac.nowcoder.com/acm/contest/40645/A

时间 名称 赛制 组别 得分 排名
2022.10.04 2022牛客OI赛前集训营(第一场) OI 提高组 165/400 28

A.

既然放在第一题那就应该不会太难,无脑暴力 O(n4)O(n^4) 能拿30pts,稍微加一个二分答案就能拿下70pts。

这里只讲正解:

a,b,c,da,b,c,d 分别为左上角,右上角,左下角,右下角四盏灯的耗电量,x1,x2,x3,x4x_1,x_2,x_3,x_4 分别为左上角,右上角,左下角,右下角四盏灯要求的亮度之和,那么一定满足下列式子:

a=k×4+tmpmaxn=a+b+c+da=k\times4+tmp,maxn=a+b+c+d,得:

{a+b2+c2+d4x1b+a2+d2+c4x2c+a2+d2+b4x3d+b2+c2+a4x4\begin{cases}a+\lfloor\dfrac{b}{2}\rfloor+\lfloor\dfrac{c}{2}\rfloor+\lfloor\dfrac{d}{4}\rfloor\le x_1\\b+\lfloor\dfrac{a}{2}\rfloor+\lfloor\dfrac{d}{2}\rfloor+\lfloor\dfrac{c}{4}\rfloor\le x_2\\c+\lfloor\dfrac{a}{2}\rfloor+\lfloor\dfrac{d}{2}\rfloor+\lfloor\dfrac{b}{4}\rfloor\le x_3\\d+\lfloor\dfrac{b}{2}\rfloor+\lfloor\dfrac{c}{2}\rfloor+\lfloor\dfrac{a}{4}\rfloor\le x_4\end{cases}

\Rrightarrow {k×4+tmp+b2+c2+maxnk×4tmpbc4=k×3+tmp+b2+c2+maxntmpbc4x1b+k×4+tmp2+maxnk×4tmpbc2+c4=b+tmp2+maxnktmpbc2+c4x2c+k×4+tmp2+maxnk×4tmpbc2+b4=c+tmp2+maxnktmpbc2+b4x3maxnk×4tmpbc+b2+c2+k×4+tmp4=maxnk×3tmpbc+b2+c2+tmp4x4\begin{cases}k\times4+tmp+\lfloor\dfrac{b}{2}\rfloor+\lfloor\dfrac{c}{2}\rfloor+\lfloor\dfrac{maxn-k\times4-tmp-b-c}{4}\rfloor=k\times3+tmp+\lfloor\dfrac{b}{2}\rfloor+\lfloor\dfrac{c}{2}\rfloor+\lfloor\dfrac{maxn-tmp-b-c}{4}\rfloor\le x_1\\b+\lfloor\dfrac{k\times4+tmp}{2}\rfloor+\lfloor\dfrac{maxn-k\times4-tmp-b-c}{2}\rfloor+\lfloor\dfrac{c}{4}\rfloor=b+\lfloor\dfrac{tmp}{2}\rfloor+\lfloor\dfrac{maxn-k-tmp-b-c}{2}\rfloor+\lfloor\dfrac{c}{4}\rfloor\le x_2\\c+\lfloor\dfrac{k\times4+tmp}{2}\rfloor+\lfloor\dfrac{maxn-k\times4-tmp-b-c}{2}\rfloor+\lfloor\dfrac{b}{4}\rfloor=c+\lfloor\dfrac{tmp}{2}\rfloor+\lfloor\dfrac{maxn-k-tmp-b-c}{2}\rfloor+\lfloor\dfrac{b}{4}\rfloor\le x_3\\maxn-k\times4-tmp-b-c+\lfloor\dfrac{b}{2}\rfloor+\lfloor\dfrac{c}{2}\rfloor+\lfloor\dfrac{k\times4+tmp}{4}\rfloor=maxn-k\times3-tmp-b-c+\lfloor\dfrac{b}{2}\rfloor+\lfloor\dfrac{c}{2}\rfloor+\lfloor\dfrac{tmp}{4}\rfloor\le x_4\end{cases}

好丑的式子……

那么接下来只要二分 maxnmaxn ,枚举 b,c,tmpb,c,tmp 即可。

对于中间两个式子直接判合法性,对于上下两个化简后可以得出 kk 的范围 [l,r][l,r] (若 r<lr<lr<0r<0maxnl×4bc<0maxn-l\times4-b-c<0 均为不合法)。

时间复杂度 O(n2logn)O(n^2logn)

贴下代码吧:

#include<bits/stdc++.h>
using namespace std;

int x[5];
bool check(int maxn)
{
    for(int b=maxn;~b;--b)
        for(int c=maxn-b;~c;--c)
            for(int tmp=0;tmp<=min(3,maxn-b-c);++tmp)
            {
                if(b+tmp/2+(maxn-b-c-tmp)/2+c/4<x[2]) continue;
                if(c+tmp/2+(maxn-b-c-tmp)/2+b/4<x[3]) continue;
                int now=max(0,x[1]-tmp-b/2-c/2-(maxn-b-c-tmp)/4);
                int l=(now%3)?(now/3+1):(now/3);
                now=maxn-tmp-b-c+tmp/4+b/2+c/2-x[4];
                if(now<0) continue;
                int r=now/3;
                if(maxn-l*4-tmp-b-c<0) continue;
                if(r<l || r<0) continue;
                return 1;
            }
    return 0;
}
int main()
{
    int ans=0;
    for(int i=1;i<=4;++i) scanf("%d",&x[i]),ans+=x[i];
    int l=0,r=ans;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(check(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    cout<<ans;
    return 0;
}

B.

考虑按位拆数,对于每一位只需考虑当前节点及其儿子中该位为 11 的数。

合法方案数 2cnt12^{cnt-1} ,其他位置随便选有 2ncnt2^{n-cnt} 种情况。

于是当前块对答案的贡献 i=029(num[i]>0)×2i×2cnt1×2ncnt\sum_{i=0}^{29} (num[i]>0)\times2^i\times2^{cnt-1}\times2^{n-cnt}

当然还要减去只有一只蚂蚁的贡献,还有根节点有一些恶心的特判。

代码:

#include<bits/stdc++.h>
using namespace std;

const int MAXN=1e5+5;
const int MOD=1e9+7;
vector<int>G[MAXN];
int a[MAXN],two[MAXN];
int n,ans;
void add(int x,int *num)
{
    for(int i=0;i<30;++i) if(x&(1<<i)) ++num[i];
}
void dfs(int x)
{
    int num[30],cnt=0;
    memset(num,0,sizeof(num));
    add(a[x],num),++cnt;
    for(auto y:G[x]) dfs(y),add(a[y],num),++cnt;
    if(cnt==1) return;
    int now=(x==1?two[cnt-2]:two[cnt-1]),all=(x==1?two[n-cnt]:two[n-cnt-1]);
    for(int i=0;i<30;++i)
    {
        if(!num[i]) continue;
        int tmp=now;
        if(x==1 && a[x]&(1<<i) && num[i]==1) (tmp<<=1)%=MOD;
        (ans+=(1ll<<i)*tmp%MOD*all%MOD)%=MOD;
    }
    (ans-=(long long)a[x]*all%MOD-MOD)%=MOD;
    if(x!=1) for(auto y:G[x]) (ans-=(long long)a[y]*all%MOD-MOD)%=MOD;
}
int main()
{
    cin>>n;
    two[0]=1;
    for(int i=1;i<=n;++i) two[i]=(two[i-1]<<1)%MOD;
    for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    int x;
    for(int i=2;i<=n;++i) scanf("%d",&x),G[x].push_back(i);
    dfs(1);
    cout<<ans;
    return 0;
}

C.字符串

考场打了个 O(n4)O(n^4) 的dp,还过了50pts……

说实话OI赛制真的不是特别敢打贪心(尤其是放在T3的情况下)。

贪心构造的话就是先让尽量多的AB交错排,然后多的再凑上长度为 a/ba/bA/BA/B 串。

代码(贴了些题解没有的注释):

#include<bits/stdc++.h>
using namespace std;

int main()
{
    int T;
    cin>>T;
    int n,m,a,b,c;
    while(T--)
    {
        scanf("%d %d %d %d %d",&n,&m,&a,&b,&c);
        int ans=(n-1)/a+(m-1)/b+2,tmp=min(n,m/c);  //先全A后全B的情况
        for(int i=1;i<=tmp;++i)
        {
            int sum=i*2; //间隔有i*2个(包括第1个与第0个)
            if(c>b) sum+=(c-1)/b*i;  //在构造长度为c的串时顺便贡献的答案
            if(n-i>=1) ++sum,sum+=(n-i-1)/a;  //前面是ABBBABBB的串,结尾全放A
            if(m-c*i>=1)   //前面是BBBABBBA的串,结尾全放B
            {
                ++sum;  //末尾扔一个B
                int lst=m-c*i-1;
                if(c<=b)                          //把前面长度为c的串凑整
                {
                    int tmp=min(i,lst/(b+1-c));
                    lst-=tmp*(b+1-c),sum+=tmp;
                }
                else
                {
                    int tmp=min(i,lst/(b-(c-1)%b));
                    lst-=tmp*(b-(c-1)%b),sum+=tmp;
                }
                sum+=lst/b;  //剩下的全扔到末尾
            }
            ans=max(ans,sum);
        }
        printf("%d\n",ans);
    }
    return 0;
}

D.奇怪的函数

本题数据太弱纯暴力能拿30pts,考场为了过特殊性质2的点加了些奇怪的优化然后写挂了剩下5pts……(OI赛制的魅力)

可以看出它是一个分段函数,min和max是限制初始值的,用线段树维护初始值的限制范围以及总共加了多少即可。

代码:

#include<bits/stdc++.h>
using namespace std;

#define ls(p) p<<1
#define rs(p) p<<1|1
const int MAXN=1e5+5;
const int INF=1e9;
struct Segment_Tree
{
    int l,r,L=-INF,R=INF,sum=0;
}tree[MAXN<<2];
void build(int p,int l,int r)
{
    tree[p].l=l,tree[p].r=r;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(ls(p),l,mid),build(rs(p),mid+1,r);
}
void push_up(int p)
{
    tree[p].L=max(min(tree[ls(p)].L,tree[rs(p)].R-tree[ls(p)].sum),tree[rs(p)].L-tree[ls(p)].sum);
    tree[p].R=max(min(tree[ls(p)].R,tree[rs(p)].R-tree[ls(p)].sum),tree[rs(p)].L-tree[ls(p)].sum);
    tree[p].sum=tree[ls(p)].sum+tree[rs(p)].sum;
}
void modify(int p,int pos,int opt,int val)
{
    if(tree[p].l==tree[p].r)
    {
        switch(opt)
        {
            case 1: tree[p]=(Segment_Tree){tree[p].l,tree[p].r,-INF,INF,val}; break;
            case 2: tree[p]=(Segment_Tree){tree[p].l,tree[p].r,-INF,val,0}; break;
            case 3: tree[p]=(Segment_Tree){tree[p].l,tree[p].r,val,INF,0}; break;
        }
        return;
    }
    int mid=(tree[p].l+tree[p].r)>>1;
    if(pos<=mid) modify(ls(p),pos,opt,val);
    else modify(rs(p),pos,opt,val);
    push_up(p);
}
int main()
{
    int n;
    cin>>n;
    build(1,1,n);
    int pos,opt,val;
    for(int i=1;i<=n;++i) scanf("%d %d",&opt,&val),modify(1,i,opt,val);
    int q;
    cin>>q;
    while(q--)
    {
        scanf("%d",&opt);
        if(opt!=4) scanf("%d %d",&pos,&val),modify(1,pos,opt,val);
        else scanf("%d",&val),printf("%d\n",max(min(val,tree[1].R),tree[1].L)+tree[1].sum);
    }
    return 0;
}

后记:这是第一次打牛客OI集训营,题目难度大概有CSP-S级别,不过感觉部分分设计的不太合理,导致分差拉的很大。

笔者能力有限,有未述详尽或有误之处,欢迎指正。

全部评论
hack:1 1 4 5 output:6
1 回复 分享
发布于 2022-10-06 09:04 江西
T1hack:2 3 1 2 答案:4
1 回复 分享
发布于 2022-10-11 08:10 山东

相关推荐

11-04 14:10
东南大学 Java
_可乐多加冰_:去市公司包卖卡的
点赞 评论 收藏
分享
评论
3
收藏
分享
牛客网
牛客企业服务