COCICONTEST# 29.11.2014# 题解+总结

来源:互联网 发布:福禄克网络 dsx 8000 编辑:程序博客网 时间:2024/06/05 19:27

NOIP临近,刷刷题。
这场比赛下来明显觉得自己代码能力不够,比如第五题stogovi很明显的LCA,也想到是用LCA,然而并木有写出来,对模板的应用不够熟练。在思维方面不够严谨,比如第四题coci,没有意识到题目给的选手分数范围的意义,也可能是因为读题不够仔细吧。

在时间分配上还存在严重问题,前面的水题不能保证正确性,想错或者在某个地方手抽打错,在调试上花了很多时间以至于想后面的题时有一点慌。这也可能是因为码代码的速度不够快吧。

第一题:strojopis
打表模拟,水题一道。

#include <iostream>#include <cstdio>using namespace std;int num[105] ;char a ;int cnt[10] ;int main(){    num[49]=num[81]=num[65]=num[90]=1 ;    num[50]=num[87]=num[83]=num[88]=2 ;    num[51]=num[69]=num[68]=num[67]=3 ;    num[52]=num[82]=num[70]=num[86]=num[53]=num[84]=num[71]=num[66]=4 ;    num[54]=num[89]=num[72]=num[55]=num[85]=num[74]=num[77]=num[78]=5 ;    num[56]=num[73]=num[75]=num[44]=6 ;    num[57]=num[79]=num[76]=num[46]=7 ;    num[48]=num[80]=num[59]=num[47]=num[45]=num[91]=num[39]=num[61]=num[93]=8 ;    while(~scanf("%c",&a))        ++cnt[num[a]] ;    for(int i=1;i<=8;++i)        printf("%d\n",cnt[i]);    return 0;}

第二题:dom
不说了,水题一道。
但蒟蒻在写代码的时候习惯不好,明明可以用while循环写的,却手抽用递归,爆栈不解释。

#include <iostream>#include <cstdio>#include <cstdlib>#define MAXN 100010using namespace std;int n ,m ,p ,a ,b ,next[MAXN] ,ans ;bool vis[MAXN] ;int main(){    scanf("%d%d%d",&n,&m,&p);    for(int i=1;i<=n;++i)    {        scanf("%d%d",&a,&b);        if(!next[b])            next[b]=a;    }    vis[p]=1;    while(next[p])    {        ++ans;        p=next[p];        if(vis[p])        {            ans=-1;            break;        }        vis[p]=1;    }    printf("%d\n",ans);    return 0;}

第三题:silueta
模拟水题一道,但蒟蒻竟然分都不分 %>_<%
枚举每一列,寻找到最高位置。由于该建筑物的边框只会是连续的一段,所有最高位置就是边框的终点。比较它和前一列的高度,周长肯定要加上它俩的高度差以及这一列的宽度(就是1)。如果出现了这一列比前一列低,就应该去填前一列。所以在代码的31行进行分类。

#include <iostream>#include <cstdio>#include <cstring>#define abs(a) ((a)>0?(a):-(a))#define MAXN 1000#define MAXM 10005using namespace std;int ans ,n ,l[MAXM] ,r[MAXM] ,h[MAXM] ,maxh[MAXM] ,tmp1 ,tmp2 ,tmp3 ;char map[MAXN+5][MAXN+5] ;int main(){    scanf("%d",&n);    memset(map,'.',sizeof map);    tmp1=MAXM ;    for(int i=1;i<=n;++i)    {        scanf("%d%d%d",&l[i],&r[i],&h[i]);        --r[i];        tmp1=min(tmp1,l[i]) ,tmp2=max(tmp2,r[i]) ;    }    for(int i=tmp1;i<=tmp2+1;++i)    {        for(int j=1;j<=n;++j)            if(l[j]<=i&&i<=r[j])                maxh[i]=max(maxh[i],h[j]);        ans+=abs(maxh[i]-maxh[i-1]);        if(maxh[i]>0)++ans;        int end=max(maxh[i],maxh[i-1]);        if(maxh[i]<maxh[i-1])            for(int j=min(maxh[i],maxh[i-1]);j<=end;++j)                map[j][i-1]='#';        else            for(int j=min(maxh[i],maxh[i-1]);j<=end;++j)                map[j][i]='#';        map[maxh[i]][i]='#';        tmp3=max(tmp3,maxh[i]);    }    printf("%d\n",ans);    for(int i=tmp3;i>0;--i)    {        for(int j=tmp1;j<=tmp2;++j)            printf("%c",map[i][j]);        puts("");    }    for(int j=tmp1;j<=tmp2;++j)        printf("*");    puts("");    return 0;}

第四题:honi
题目大意:给你n个选手的前两场比赛的分数,计算出他的总排名的范围。若选手A的前两场的分数都比选手B高,那么A第三场的分数也会比B高。分数的范围为[0,650]

蒟蒻一开始以为分数的范围并没有用,就用树状数组乱YY了一种方法wa了4个点。事实上分数的范围是有用的,见下面的例子。
2
650 1
0 1
若没有分数范围,那么两个人的排名都为[1,2],但答案为[1,1][1,2],因为即使第一个人第三场得0分,第二个人得650分,第一个人也还是第一名。

正解:令s[i][j]表示第一场得分i第二场得分j总人数。令num[i][j]表示第一场得分x<=i并且第二场得分y<=j的总人数。那么他的最高排名就是比他得分都高的总人数,最低排名就是ans=n比他得分低的总人数。在算最低排名的时候要特判得分为最高分的情况(原因见上面的例子)。

#include <iostream>#include <cstdio>#define MAXN 500005#define MAXM 650using namespace std;int n ,a[MAXN] ,b[MAXN] ,ans ;int num[MAXM+5][MAXM+5] ,s[MAXM+5][MAXM+5] ;int solve(int x1,int y1,int x2,int y2){    if(x2<0||y2<0)        return 0;    int ans=num[x2][y2] ;    if(y1>0)        ans-=num[x2][y1-1];    if(x1>0)        ans-=num[x1-1][y2] ;    if(x1>0&&y1>0)        ans+=num[x1-1][y1-1] ;    return ans;}int main(){    scanf("%d",&n);    for(int i=1;i<=n;++i)    {        scanf("%d%d",&a[i],&b[i]);        ++s[a[i]][b[i]] ;    }    for(int i=0;i<=MAXM;++i)        for(int j=0;j<=MAXM;++j)        {            num[i][j]=s[i][j];            if(i>0)                num[i][j]+=num[i-1][j];            if(j>0)                num[i][j]+=num[i][j-1];            if(i>0&&j>0)                num[i][j]-=num[i-1][j-1] ;        }    for(int i=1;i<=n;++i)    {        printf("%d ",solve(a[i]+1,b[i]+1,MAXM,MAXM)+1);        ans=n-solve(0,0,a[i]-1,b[i]-1) ;        if(a[i]==MAXM)ans-=s[0][b[i]];        if(b[i]==MAXM)ans-=s[a[i]][0];        printf("%d\n",ans);    }    return 0;}

第五题:stogovi
很明显的LCA。
如果是加入元素i入栈,那么i号节点的父亲就是他自己。
而对于操作b,即弹出栈顶元素,i的父亲就变为v的父亲,同时i的祖先也要跟着改变。(这个转化当时木有想到就木有写出来%>_<%)
而对于操作c,就找它俩的最近公共祖先,输出祖先的长度即可。

#include <iostream>#include <cstdio>#define MAXN 300005#define MAXK 20using namespace std;int n ,fa[MAXN] ,f[MAXN][MAXK] ,v ,w ,size[MAXN] ;char word[5] ;void adjust(int &u,int d){    for(int j=MAXK-1;j>=0;--j)        if(size[f[u][j]]>=d)            u=f[u][j] ;}int lca(int u,int v){    if(size[u]<size[v])        swap(u,v);    if(size[u]>size[v])        adjust(u,size[v]);    if(u==v)        return u;    for(int j=MAXK-1;j>=0;--j)        if(f[u][j]!=f[v][j])            u=f[u][j] ,v=f[v][j] ;    return f[u][0] ;}int main(){    scanf("%d",&n);    for(int i=1;i<=n;++i)    {        scanf("%s%d",word,&v);        v=fa[v] ;        fa[i]=v ;        if(word[0]=='a')        {            fa[i]=i ;            size[i]=size[v]+1 ;            f[i][0]=v ;            for(int j=1;j<MAXK;++j)                f[i][j]=f[f[i][j-1]][j-1] ;        }        else if(word[0]=='b')        {            printf("%d\n",fa[i]);            fa[i]=f[fa[i]][0] ;        }        else        {            scanf("%d",&w);            w=fa[w] ;            printf("%d\n",size[lca(v,w)]);        }    }    return 0;}

第六题:kamioni
当时在考场上就想过将所有询问保存起来,然后所有卡车一起跑,处理卡车掉头的地方,每一次都对询问处理一次。然而蒟蒻却更新了所有的卡车,就发现还没爆搜跑得快,于是果断写暴力= =

正解:将每一辆卡车的掉头地点、时间以及方向存起来,按时间排序。对于一个询问,只需要处理一个卡车就行了。所以将这个询问放在掉头数小的那辆车上。更新跟它有询问的车就行了。

接下来分析时间复杂度。
若一辆卡车的掉头数超过了sqrt(k),那么它的询问肯定不会超过sqrt(k)。假设它的询问超过了sqrt(k),那么那些对应的卡车的掉头次数超过了sqrt(k),总和就已经超过了k,矛盾。
若一辆卡车的掉头数少于了sqrt(k),虽然它的询问可能会超过sqrt(k),但是掉头数少于sqrt(k)
这样均摊下来,时间复杂度为O(ksqrt(k))

#include <iostream>#include <cstdio>#include <algorithm>#include <map>#include <vector>#define abs(a) ((a)>0?(a):-(a))#define MAXN 100005#define MAXK 300005#define pii pair<int,int>#define LL long long intusing namespace std;map<pii,vector<int> >sameask;int n ,m ,a ,b ,pos1 ;int num[MAXN] ,dir[MAXN] ,ans[MAXN] ;LL ti ;struct Car{    int id ,pos ,dir ;    LL t;    bool operator < (const Car &a)const    {        if(t!=a.t)return t<a.t;        return dir>a.dir;    }}car[MAXK] ,truck[MAXN] ;struct node{    int id ,v ;    node *next ;}edge[MAXN] ,*adj[MAXN] ,*code=edge ;int check_left(const Car &a,const Car &b,LL t,int dir){    if(b.dir==0&&t>=b.t)return 0;    int apos=a.pos ,bpos=b.pos ;    if(b.dir==0)        apos+=(a.t-b.t)*(-dir);    else bpos+=(a.t-b.t)*b.dir;    if(apos<bpos)        return 1;    return -1;}int main(){    scanf("%d%d",&n,&m);    for(int i=1;i<=n;++i)    {        scanf("%d%d",&num[i],&a);        ti=0;        for(int j=1;j<num[i];++j)        {            scanf("%d",&b);            Car e={i,a,a<b?1:-1,ti};            if(j==1)                truck[i]=e;            else car[pos1++]=e;            ti+=abs(a-b) ,a=b ;        }        Car e={i,a,0,ti};        car[pos1++]=e;    }    for(int i=0;i<m;++i)    {        scanf("%d%d",&a,&b);        if(a>b)swap(a,b);        sameask[pii(a,b)].push_back(i);        if((int)sameask[pii(a,b)].size()>1)continue;        if(num[a]>num[b])swap(a,b);        code->v=b ,code->id=i ,code->next=adj[a] ;        adj[a]=code ;        ++code ;        dir[i]=truck[a].pos<truck[b].pos?1:-1;    }    sort(car,car+pos1);    int id ,temp ,dir1 ;    for(int i=0;i<pos1;++i)    {        id=car[i].id ;        ti=truck[id].t ,dir1=truck[id].dir ;        truck[id]=car[i];        for(node *p=adj[id];p!=NULL;p=p->next)        {            temp=check_left(truck[id],truck[p->v],ti,dir1);            ans[p->id]+=(dir[p->id]*temp==-1);            dir[p->id]=temp;        }    }    int res ;    for(map<pii,vector<int> >::iterator p=sameask.begin();p!=sameask.end();p++)    {        res=ans[p->second[0]];        for(int i=1;i<p->second.size();++i)            ans[p->second[i]]=res;    }    for(int i=0;i<m;++i)        printf("%d\n",ans[i]);    return 0;}
0 0
原创粉丝点击