2012ACM网络预赛5

ALL ABOUT ACM 专栏收录该内容
40 篇文章 0 订阅

      从第一场到今天的第五场,每场都有那么些无奈吧,跟去年一样,还是最后一场顺手。。。肯定是昨晚玩三国杀被虐而爆人品了,其实是因为好久没玩才被虐的。。。

今天做的第一题就是08题 :hdu 4417 Super Mario

    这题一拿到,没什么想法,突然想去上厕所= =,期间想出正解,回来后果断贴模板,也就是前几天我做过的划分树。。。

    其实就是每次询问区间内小于等于一个数的数有几个,很明显符合划分树的范畴,然后只要改改query函数,没次对于大于中间的数就加上划分到左边的个数,否则不管,有个问题要处理下,就是可能区间会划分到空,还有可能只有一个元素,而且等于查找的数,要特殊处理下。。。

贴下代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int mm=111111;
int a[mm],s[22][mm],g[22][mm];
int i,j,k,n,m,t,ans,cs=0;
void build(int l,int r,int d)
{
    if(l==r)return;
    int i,t1,t2,same=0,m=(l+r)>>1;
    for(i=l;i<=r;++i)
        g[d][i]=0,same+=(s[d][i]<a[m]);
    same=m-l+1-same;
    for(t1=i=l,t2=m+1;i<=r;++i)
        if(s[d][i]==a[m]&&same)--same,s[d+1][t1++]=s[d][i],g[d][i]=1;
        else if(s[d][i]<a[m])
            s[d+1][t1++]=s[d][i],g[d][i]=1;
        else s[d+1][t2++]=s[d][i];
    for(i=l;i<r;++i)
        g[d][i+1]+=g[d][i];
    build(l,m,d+1);
    build(m+1,r,d+1);
}
void query(int L,int R,int k,int l,int r,int d)
{
    if(L>R)return;
    if(l==r)
    {
        if(a[l]<=k)++ans;
        return;
    }
    int m=(l+r)>>1,w1=0,w2=g[d][R];
    if(L>l)w2-=(w1=g[d][L-1]);
    if(a[m]>k)query(l+w1,l+w1+w2-1,k,l,m,d+1);
    else
    {
        ans+=w2;
        w1=L-l-w1;
        w2=R-L+1-w2;
        query(m+w1+1,m+w1+w2,k,m+1,r,d+1);
    }
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;++i)
        {
            scanf("%d",&a[i]);
            s[0][i]=a[i];
        }
        sort(a+1,a+n+1);
        build(1,n,0);
        printf("Case %d:\n",++cs);
        while(m--)
        {
            scanf("%d%d%d",&i,&j,&k);
            ++i,++j;
            ans=0;
            query(i,j,k,1,n,0);
            printf("%d\n",ans);
        }
    }
    return 0;
}

过完这题,马上下手做过的最多的 05题 hdu 4414 Finding crosses

好吧,这题看完后就是个水题,直接把相邻的用flood fill标记,并记录这个块有几格,长度为多少,宽度为多少,保证个数大于5,长和宽相等,并且在对应的四个位置都是#就行,也就是这个块的第一行和最后一行的中间,第一列和最后一列的中间。。。

代码:

#include<cstdio>
#include<iostream>
using namespace std;
const int mm=55;
int dx[]={0,0,-1,1};
int dy[]={-1,1,0,0};
char map[mm][mm];
int qx[mm*mm],qy[mm*mm];
int n,ans;
void bfs(int x,int y)
{
    int i,ml=y,mr=y,mt=x,mb=x,s=1,l,r=1;
    qx[0]=x,qy[0]=y,map[x][y]='-';
    for(l=0;l<r;++l)
        for(i=0;i<4;++i)
        {
            x=qx[l]+dx[i];
            y=qy[l]+dy[i];
            if(x<1||x>n||y<0||y>=n||map[x][y]!='#')continue;
            qx[r]=x,qy[r]=y,map[x][y]='-',++r;
            ml=min(ml,y);
            mr=max(mr,y);
            mt=min(mt,x);
            mb=max(mb,x);
            ++s;
        }
    if(mr-ml==mb-mt&&s>4&&s==(mr-ml+mb-mt+1)
       &&map[mt][(ml+mr)>>1]=='-'
       &&map[mb][(ml+mr)>>1]=='-'
       &&map[(mt+mb)>>1][ml]=='-'
       &&map[(mt+mb)>>1][mr]=='-')++ans;
}
int main()
{
    int i,j;
    while(scanf("%d",&n),n)
    {
        for(i=1;i<=n;++i)
            scanf("%s",map[i]);
        ans=0;
        for(i=1;i<=n;++i)
            for(j=0;j<n;++j)
                if(map[i][j]=='#')bfs(i,j);
        printf("%d\n",ans);
    }
    return 0;
}

过完以上两题,发现其他题过的人都很少,稍微看了一些题

后来选了02题 hdu 4411 Arrest

这题一开始看挺麻烦,不过马上想到一个费用流模型,首先构造个源点还有一个汇点,并把每个城市都分成两个点i和i‘,由于每次只能按编号小的开始抓,所以直接在i’到j (j>i)连上一条边,容量为1,费用为i到j的最短路。。,对于么个城市i连上0到i的变,容量为1,费用为0到i的最短路,再连上一条从i‘到汇点的边,容量为1,费用为i到0的最短路,表示回到0,然后从i到i’连上一条容量为1,费用为很大的负数,这样就保证肯定通过这条边,也就是抓了这个城市的人,最后连一条边从源点到0,容量为k,费用为0

到这里,我以为就对了,结果发现样例都过不了,多亏了这组样例啊,后来发现这样写,k个组员都必须出去走走,所有不是最优的,实际上有的组员不用出去的= =

解决方法很简单,在0和汇点直接连一条容量为k费用为0的边,表示组员呆着不动。。。

代码:

#include<cstdio>
#include<iostream>
using namespace std;
const int mm=666666;
const int mn=222;
const int oo=1e9;
int src,dest,node,edge;
int ver[mm],cost[mm],flow[mm],next[mm];
int head[mn],dis[mn],p[mn],q[mn];
int d[mn][mn];
bool vis[mn]={0};
void prepare(int _node,int _src,int _dest)
{
    node=_node,src=_src,dest=_dest;
    for(int i=0;i<node;++i)head[i]=-1;
    edge=0;
}
void addedge(int u,int v,int f,int c)
{
    ver[edge]=v,flow[edge]=f,cost[edge]=c,next[edge]=head[u],head[u]=edge++;
    ver[edge]=u,flow[edge]=0,cost[edge]=-c,next[edge]=head[v],head[v]=edge++;
}
bool Spfa()
{
    int i,u,v,l,r=0,tmp;
    for(i=0;i<node;++i)dis[i]=oo;
    dis[q[r++]=src]=0;
    p[src]=p[dest]=-1;
    for(l=0;l!=r;(++l==mn)?l=0:l)
        for(i=head[u=q[l]],vis[u]=0;i>=0;i=next[i])
            if(flow[i]&&dis[v=ver[i]]>(tmp=dis[u]+cost[i]))
            {
                dis[v]=tmp;
                p[v]=i^1;
                if(vis[v])continue;
                vis[q[r++]=v]=1;
                if(r==mn)r=0;
            }
    return p[dest]>-1;
}
int Spfaflow()
{
    int i,delta,ret=0;
    while(Spfa())
    {
        for(i=p[dest],delta=oo;i>=0;i=p[ver[i]])
            if(flow[i^1]<delta)delta=flow[i^1];
        for(i=p[dest];i>=0;i=p[ver[i]])
            flow[i]+=delta,flow[i^1]-=delta;
        ret+=delta*dis[dest];
    }
    return ret;
}
int main()
{
    int i,j,k,n,m,w;
    while(scanf("%d%d%d",&n,&m,&w),n+m+w)
    {
        prepare(n+n+3,n+n+1,n+n+2);
        for(i=0;i<=n;++i)
            for(j=0;j<=n;++j)
                d[i][j]=1e6;
        while(m--)
        {
            scanf("%d%d%d",&i,&j,&k);
            d[i][j]=d[j][i]=min(d[i][j],k);
        }
        for(k=0;k<=n;++k)
            for(i=0;i<=n;++i)
                for(j=0;j<=n;++j)
                    d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
        addedge(src,0,w,0);
        addedge(0,dest,w,0);
        for(i=1;i<=n;++i)
        {
            addedge(0,i,1,d[0][i]);
            addedge(i,i+n,1,-1e7);
            addedge(i+n,dest,1,d[0][i]);
        }
        for(i=1;i<=n;++i)
            for(j=i+1;j<=n;++j)
                addedge(i+n,j,1,d[i][j]);
        printf("%d\n",Spfaflow()+n*10000000);
    }
    return 0;
}

好吧,交上去1Y了,此时排名还行,赶紧继续做,又是选题问题,06题还没做,我一看是贪心,但是不会证明,而且写法复杂,果断不做,直接选了最后一题,也

就是10题  hdu 4419 Colourful Rectangle

这题一看似曾相识,赶紧翻翻以前做过的题,确实有比较相似的,不过这题不大一样,以前是统计不相同的个数,这次是统计颜色的面积,不过也好处理,剩下就是线段树了

首先离散化y轴,然后扫描线从x最小到最大,逐步插入线段,线段分插入和删除,线段树组要保存当前线段其中颜色的长度,还有当前线段图上的颜色的个数,颜色的计算方法用二进制,R个数大于0就加1,否则加0,G大于0就加2,B加4,。。。加完的值就是当前线段图上的颜色,具体还得和子线段合并,如果子线段没有颜色,那么当前线段就只有一种颜色,长度为线段长度,如果子线段有颜色,并且这个颜色并上当前线段的颜色,改变了颜色,就减少当前线段的颜色,增加新的颜色的长度,具体看代码吧:

PS:这题本来应该这样就过了,但是我调试了近半个小时,找不到错,最后把统计答案从double改成long long,居然就过了,具体我也找不到原因。。。

总之被坑了近半个点;

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define ls rt<<1
#define rs rt<<1|1
#define lson l,m,ls
#define rson m,r,rs
using namespace std;
const int mm=22222;
const int mn=mm<<2;
int ot[]={1,2,4,3,5,6,7};
struct seg
{
    int x,y1,y2,val;
}g[mm];
int len[mn][8],y[mm],L,R;
long long ans[8];
int t[mn][4],val;
void build()
{
    memset(len,0,sizeof(len));
    memset(t,0,sizeof(t));
    memset(ans,0,sizeof(ans));
}
void updata(int l,int r,int rt)
{
    if(L<=y[l]&&R>=y[r])val<0?--t[rt][-val]:++t[rt][val];
    else
    {
        int m=(l+r)>>1;
        if(L<y[m])updata(lson);
        if(R>y[m])updata(rson);
    }
    int ste=(t[rt][1]>0)|((t[rt][2]>0)<<1)|((t[rt][3]>0)<<2);
    if(ste>0)
    {
        for(int i=1;i<8;++i)len[rt][i]=0;
        len[rt][ste]=y[r]-y[l];
        for(int i=1;i<8;++i)
            if((i|ste)!=ste)
            {
                int tmp=(len[rt<<1][i]+len[rt<<1|1][i]);
                len[rt][i|ste]+=tmp;
                len[rt][ste]-=tmp;
            }
    }
    else if(l>=r)memset(len[rt],0,sizeof(len[rt]));
    else for(int i=1;i<8;++i)len[rt][i]=(len[rt<<1][i]+len[rt<<1|1][i]);
}
bool cmp(seg a,seg b)
{
    return a.x<b.x;
}
int main()
{
    int i,j,m,n,t,cs=0;
    char co[8];
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(i=0;i<n;++i)
        {
            scanf("%s%d%d%d%d",co,&g[i].x,&y[i],&g[i+n].x,&y[i+n]);
            if(co[0]=='R')j=1;
            if(co[0]=='G')j=2;
            if(co[0]=='B')j=3;
            g[i].y1=y[i],g[i].y2=y[i+n],g[i].val=j;
            g[i+n].y1=y[i],g[i+n].y2=y[i+n],g[i+n].val=-j;
        }
        sort(y,y+n+n);
        sort(g,g+n+n,cmp);
        for(m=i=0;i<n+n;++i)
            if(y[m]<y[i])y[++m]=y[i];
        build();
        for(i=0;i<n+n;++i)
        {
            L=g[i].y1,R=g[i].y2,val=g[i].val;
            updata(0,m,1);
            if(g[i].x<g[i+1].x)
                for(j=1;j<8;++j)
                    ans[j]+=(long long)(g[i+1].x-g[i].x)*(long long)len[1][j];
        }
        printf("Case %d:\n",++cs);
        for(j=1;j<8;++j)
            printf("%I64d\n",ans[ot[j-1]]);
    }
    return 0;
}


做完这些题,已经剩一个小时了,只能拼人品了,随便挑了一题觉得可以做的,也就是07题,然后开始搞。。。

不过大概是注定要悲剧的,搞到一半就发现没法搞了,然后只能继续随便写写,做做样子了。。。

今天总体上发挥良好,把所有水题都A掉了,呵呵

网络赛到此全部终结,但是一切才刚刚开始,今年或许是最后一年,希望一切顺利。。。

  • 0
    点赞
  • 25
    评论
  • 0
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

评论 25 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

Pira

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值