zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    Leetcode Binary Tree Preorder Traversal
    Leetcode Minimum Depth of Binary Tree
    Leetcode 148. Sort List
    Leetcode 61. Rotate List
    Leetcode 86. Partition List
    Leetcode 21. Merge Two Sorted Lists
    Leetcode 143. Reorder List
    J2EE项目应用开发过程中的易错点
    JNDI初认识
    奔腾的代码
  • 原文地址:https://www.cnblogs.com/Zinn/p/15094411.html
Copyright © 2011-2022 走看看