• 2021牛客暑期多校训练营6


    比赛链接:https://ac.nowcoder.com/acm/contest/11257

    F,H,I,10,还行。后期一直在做J,但还是没做出来。

    F

    分析:

    构造题。一种方法是把牛排按( t )从大到小排序,再限制一下盘子的时长( tim ),然后往盘子里填充;

    ( tim )肯定至少是用时最多的牛排的( t );填充过程就是如果超出盘子时长,就把它分成两半,一半填在当前盘子最后,另一半填在下一个盘子最前;这两段时间不会相交,因为如果相交,说明这个牛排的( t > tim),是不会发生的。

    盘子要尽量填满,所以( tim )最好是( sum(t) / m )向上取整;这个和最大( t )取一个( max ),就是最终的( tim )了。

    注意( long long )。

    代码如下:

    #include<iostream>
    #include<algorithm>
    #define ll long long
    using namespace std;
    int const N=1e5+5;
    int n,m;
    struct Nd{
        int num,p[2]; ll l[2],r[2];
    }ans[N];
    struct It{
        int t,id;
    }a[N];
    bool cmp(It x,It y){return x.t>y.t;}
    ll mx(int x,ll y){return x>y?x:y;}
    int main()
    {
        scanf("%d%d",&n,&m); ll tim=0;
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i].t),a[i].id=i,tim+=a[i].t;
        sort(a+1,a+n+1,cmp);
        int pan=1; ll nw=0; tim=mx(a[1].t,(tim+m-1)/m);//
        for(int i=1;i<=n;i++)
        {
            int id=a[i].id; ll t=a[i].t;
            if(nw+t<=tim)
            {
                ans[id].num=1; ans[id].p[0]=pan;
                ans[id].l[0]=nw; ans[id].r[0]=nw+t;
                nw+=t;
            }
            else
            {
                ans[id].num=2; ans[id].p[0]=pan;
                ans[id].l[0]=nw; ans[id].r[0]=tim;
                t-=(tim-nw); nw=0; pan++;
                ans[id].p[1]=pan; ans[id].l[1]=nw; ans[id].r[1]=nw+t;
                nw=t;
            }
            if(nw==tim)pan++,nw=0;
        }
        for(int i=1;i<=n;i++)
        {
            printf("%d ",ans[i].num);
            if(ans[i].num==1)printf("%d %lld %lld
    ",ans[i].p[0],ans[i].l[0],ans[i].r[0]);
            else
            {
                if(ans[i].l[0]<ans[i].l[1])
                    printf("%d %lld %lld %d %lld %lld
    ",ans[i].p[0],ans[i].l[0],ans[i].r[0],ans[i].p[1],ans[i].l[1],ans[i].r[1]);
                else
                    printf("%d %lld %lld %d %lld %lld
    ",ans[i].p[1],ans[i].l[1],ans[i].r[1],ans[i].p[0],ans[i].l[0],ans[i].r[0]);
            }
        }
        return 0;
    }
    me

    H

    分析:

    由于兔子移动位置有周期性,所以最多有( d*d )个起点;在( (0,0) -- (d,d) )这个正方形里的起点决定了兔子可以走的所有位置;

    所以把所有矩形都用这个( d*d )网格分割,放在( (0,0) -- (d,d) )这个正方形里,找一个没有被覆盖的点作为起点;这就需要用线段树维护矩形面积并了。

    分割矩形的过程老是写不对……最后借鉴了下别人的写法才过。

    代码如下:

    #include<iostream>
    #include<vector>
    #define pb push_back
    #define ls (u<<1)
    #define rs ((u<<1)|1)
    #define mid ((l+r)>>1)
    using namespace std;
    int const N=1e5+5,inf=1e9;
    int n,d,tr[N<<2],lz[N<<2];
    struct Nd{
        int l,r,s;
    };
    vector<Nd>v[N];
    int rd()
    {
        int ret=0,f=1; char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
        while(c>='0'&&c<='9')ret=(ret<<3)+(ret<<1)+c-'0',c=getchar();
        return ret*f;
    }
    void f(int u,int s){tr[u]+=s; lz[u]+=s;}
    void pdn(int u)
    {
        if(!lz[u])return;
        f(ls,lz[u]); f(rs,lz[u]); lz[u]=0;
    }
    void pup(int u){tr[u]=min(tr[ls],tr[rs]);}
    void upt(int u,int l,int r,int ql,int qr,int s)
    {
        if(l>=ql&&r<=qr){f(u,s); return;}
        pdn(u);
        if(mid>=ql)upt(ls,l,mid,ql,qr,s);
        if(mid<qr)upt(rs,mid+1,r,ql,qr,s);
        pup(u);
    }
    /*
    int qry(int u,int l,int r,int ql,int qr)
    {
        if(l>=ql&&r<=qr)return tr[u];
        pdn(u); int ret=inf;
        if(mid>=ql)ret=min(ret,qry(ls,l,mid,ql,qr));
        if(mid<qr)ret=min(ret,qry(rs,mid+1,r,ql,qr));
        return ret;
    }
    */
    int fnd(int u,int l,int r)
    {
        if(l==r)return l;
        pdn(u);
        if(tr[ls]==0)return fnd(ls,l,mid);
        else return fnd(rs,mid+1,r);
    }
    void add(int x1,int y1,int x2,int y2)
    {
        x1=((x1%d+d)%d)+1; y1=((y1%d+d)%d)+1;
        x2=(x2%d)?(x2%d+d)%d:d; y2=(y2%d)?((y2%d+d)%d)+1:d+1;
        v[y1].pb((Nd){x1,x2,1}); v[y2].pb((Nd){x1,x2,-1});
    }
    void cal(int &x){x=(x%d+d)%d;}
    void op1(int x1,int x2,int y1,int y2){
        if(x1>=x2||y1>=y2)return;
        v[y1+1].pb((Nd){x1+1,x2,1});
        v[y2+1].pb((Nd){x1+1,x2,-1});
    }
    void op(int x1,int x2,int y1,int y2){
        if(y2-y1>=d){op1(x1,x2,0,d);return;}
        cal(y1),cal(y2);
        if(y1>y2){op1(x1,x2,y1,d),op1(x1,x2,0,y2);}
        else op1(x1,x2,y1,y2);
    }
    int main()
    {
        n=rd(); d=rd(); bool fl=1;
        for(int i=1,x1,y1,x2,y2;i<=n;i++)
        {
            x1=rd(); y1=rd(); x2=rd(); y2=rd();//注意负数
            if(x2-x1>=d&&y2-y1>=d)fl=0;
            if(fl)
            {
                /*
                if(x2-x1>=d)add(0,y1,d,y2);
                else if(y2-y1>=d)add(x1,0,x2,d);
                else
                {
                    if((x1+1)/d==x2/d&&(y1+1)/d==y2/d)add(x1,y1,x2,y2);
                    else if((x1+1)/d==x2/d)add(x1,y1,x2,d),add(x1,0,x2,y2);
                    else if((y1+1)/d==y2/d)add(x1,y1,d,y2),add(0,y1,x2,y2);
                    else add(x1,y1,d,d),add(0,y1,x2,d),add(x1,0,d,y2),add(0,0,x2,y2);
                }
                */
                if(x2-x1>=d){op(0,d,y1,y2);continue;}
                cal(x1),cal(x2);
                if(x1>x2){op(x1,d,y1,y2),op(0,x2,y1,y2);}
                else op(x1,x2,y1,y2);
            }
        }
        if(!fl){printf("NO
    "); return 0;}
        for(int y=1;y<=d;y++)
        {
            for(Nd it:v[y])upt(1,1,d,it.l,it.r,it.s);
            //if(qry(1,1,d,1,d))continue;
            if(tr[1])continue;
            int ax=fnd(1,1,d);
            printf("YES
    %d %d
    ",ax-1,y-1);
            return 0;
        }
        printf("NO
    ");
        return 0;
    }
    me

    I

    分析:

    要找一组区间,让这组区间的交恰好等于给定若干区间的并;

    那就每次把给定区间中两个相邻区间的间隙去掉即可。

    代码如下:

    #include<iostream>
    #include<algorithm>
    using namespace std;
    int const N=1005;
    int T,n,m;
    struct Nd{
        int l,r;
    }a[N],b[N];
    int rd()
    {
        int ret=0,f=1; char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
        while(c>='0'&&c<='9')ret=(ret<<3)+(ret<<1)+c-'0',c=getchar();
        return ret*f;
    }
    bool cmp(Nd x,Nd y){return x.l<y.l;}
    int main()
    {
        T=rd();
        while(T--)
        {
            n=rd(); m=rd();
            for(int i=1;i<=m;i++)
            {
                a[i].l=rd(); a[i].r=rd();
                if(a[i].l>a[i].r)a[i].r+=n;
            }
            sort(a+1,a+m+1,cmp); int cnt=0,p=1,L,R;
            while(p<=m)
            {
                L=a[p].l;
                while(p+1<=m&&a[p].r+1==a[p+1].l)p++;
                b[++cnt]=(Nd){L,a[p].r};
                p++;
            }
            printf("%d
    ",cnt);
            for(int i=1,j=cnt;i<=cnt;i++,j++)
            {
                if(j>cnt)j=1;
                L=b[i].l; R=b[j].r;
                if(R>n)R-=n;
                printf("%d %d
    ",L,R);
            }
        }
        return 0;
    }
    me

    J

    分析:

    偶数点的连通块直接加即可。考虑奇数点的连通块:

    首先,要删除(也就是贡献为负)的点彼此之间一定是孤立的;否则如果删除一个连通块,它一定包含( geq 3 )的奇数个点,那可以分出一个点,剩下偶数个点贡献还是正的。

    其次,删除的这些孤立点一共是奇数个,这样才能剩下偶数个点。

    然后,我们假设要删除的孤立点一共有( geq 3 )个,那么这些点一定都是割点,否则只删其中代价最小的那个非割点会更优。

    如果在原图中单独删除这些点中的某一个,剩下的几个子树一定存在奇数个点的(至少两个,因为剩余总点数是偶数);否则全是偶数个点的,那就没必要再删其他点了。这些奇数个点的子树里面一定还有要删除的点。

    所以对于一个要删除的点,其他要删除的点分布在它的奇数个点的子树中;

    但是这样的话总会有某些要删除的点在最边缘,它只有一边是其他的要删除的点(可以给原图建立一个圆方树,然后依据要删除的点提取一棵虚树,总有点处在叶子位置);单独删除它的话不满足上面说到的性质。

    所以假设不成立。所以一个奇数点的连通块中只会删除一个点。

    于是tarjan找到所有割点,其中所有子树都是偶数的割点可能作为答案;所有非割点也可能作为答案;比较一下它们的代价即可。

    (重温了一下tarjan,点双连通分量,无向图的割点和桥圆方树虚树;真是怀念。)

    代码如下:

    #include<iostream>
    #define ll long long
    using namespace std;
    int const N=1e6+5,inf=1e9+5;
    int T,n,m,a[N],hd[N],cnt,nxt[N<<1],to[N<<1],ccol,col[N],num[N];
    int dfn[N],cdfn,low[N],rt,son,siz[N],mn;
    bool ok[N],vis[N];
    ll ans;
    int rd()
    {
        int ret=0,f=1; char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
        while(c>='0'&&c<='9')ret=(ret<<3)+(ret<<1)+c-'0',c=getchar();
        return ret*f;
    }
    void add(int x,int y){nxt[++cnt]=hd[x]; hd[x]=cnt; to[cnt]=y;}
    void dfs(int u)
    {
        col[u]=ccol; num[ccol]++;
        for(int i=hd[u],v;i;i=nxt[i])
            if(!col[v=to[i]])dfs(v);
    }
    void tarjan(int u)
    {
        dfn[u]=low[u]=++cdfn; siz[u]=1;
        for(int i=hd[u],v;i;i=nxt[i])
        {
            if(!dfn[v=to[i]])
            {
                tarjan(v); siz[u]+=siz[v];
                low[u]=min(low[u],low[v]);
                if(u==rt)son++;
                if(low[v]>=dfn[u]&&siz[v]%2)ok[u]=0;//是割点且有奇数点子树(包括根)
            }
            else
                low[u]=min(low[u],dfn[v]);
        }
        if(u==rt&&son<=1)ok[rt]=1;
    }
    void dfs2(int u)
    {
        vis[u]=1;
        if(ok[u])mn=min(mn,a[u]);
        for(int i=hd[u],v;i;i=nxt[i])
            if(!vis[v=to[i]])dfs2(v);
    }
    void work(int x)
    {
        rt=x; son=0; cdfn=0;
        tarjan(x);
        //for(int i=1;i<=n;i++)printf("ok[%d]=%d
    ",i,ok[i]);
        mn=inf; dfs2(x);
        ans-=2*mn;
    }
    int main()
    {
        T=rd();
        while(T--)
        {
            n=rd(); m=rd(); cnt=0; ccol=0; ans=0;
            for(int i=1;i<=n;i++)
                a[i]=rd(),ans+=a[i],hd[i]=0,col[i]=0,ok[i]=1,dfn[i]=0,low[i]=0,vis[i]=0,siz[i]=0;
            for(int i=1,x,y;i<=m;i++)
                x=rd(),y=rd(),add(x,y),add(y,x);
            for(int i=1;i<=n;i++)
            {
                if(col[i])continue;
                ccol++; num[ccol]=0;
                dfs(i);
                if(num[ccol]%2)work(i);
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    me
  • 相关阅读:
    Ubuntu--更改国内镜像源(阿里、网易、清华、中科大)
    mui做直播推流及时分秒计时器的实现用例
    远程连接
    Dockerfile
    Docker-compose
    Docker基础命令
    python中执行其他的python脚本(三):
    python中执行其他的python脚本(二):
    python中执行其他的python脚本(一):
    树莓派4B源码编译opencv3.4.1
  • 原文地址:https://www.cnblogs.com/Zinn/p/15094411.html
Copyright © 2020-2023  润新知