2017"百度之星"程序设计大赛-初赛(A) 比赛总结

来源:互联网 发布:网络彩票停售 编辑:程序博客网 时间:2024/05/21 09:18

T1:小C的倍数问题(hdu6108)

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6108

题目分析:最水的一题,考场上最多人AC。题目是要我们求有多少个B使得

a0Pk+a1Pk1++ak1P+ak=x1B


a0+a1+ak1+ak=x2B

是等价的。我们可以对上下两式做差,得到:
a0(Pk1)+a1(Pk11)++ak1(P1)=(x1x2)B

由于
Pk1=(P1)(Pk1+Pk2++P+1)

所以B是P-1的因数,直接用O(P1)的时间求出P-1的因数个数即可。

CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;int t,p;int main(){    freopen("1001.in","r",stdin);    freopen("1001.out","w",stdout);    scanf("%d",&t);    while (t--)    {        scanf("%d",&p);        p--;        int k=(int)floor( sqrt( (double)p )+1e-8 ),ans=0;        for (int i=1; i<=k; i++)            if (p%i==0)            {                ans++;                if (i*i!=p) ans++;            }        printf("%d\n",ans);    }    return 0;}

T2:数据分割(hdu6109)

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6109

题目分析:一道并查集好题啊,明明是这么简单的算法考场上却少有人AC。先讲一下我考试时候的想法:假设所有xi=xj约束都在xixj之前,我们就可以用并查集来判断是否合法,现在的问题在于如果xixj的条件在xi=xj的条件之前呢?由于不等于是没有传递性的,所以这样很棘手,但可以通过合理的操作避免这个情况。我们可以先二分一个答案mid,然后对[1,mid]这一段进行CDQ分治,每一次将左边的xi=xj条件加进并查集,用右边的xixj的条件去查,再将右边的xi=xj条件加进并查集,用左边的xixj的条件查,如果没有冲突就递归下去查。注意并查集要用启发式合并,才能支持log(n)的时间内按顺序加边删边。考虑到可能要进行多次二分答案(要分成很多组数据),这样必定超时。
后来看了网上大神的做法,大概也是并查集+启发式合并。对于xixj的条件,在xi所在集合的根与xj所在集合的根之间连一条双向边;对于xi=xj的条件,设xi集合的根连出的边数<xj集合的根连出的边数,则暴力查看xi集合的根是否有连向xj集合的根的边,有则不合法,一边查看一边将xi集合连出的边加进xj集合的根里,并将原先指向xi集合的根的边改指为xj所在集合的根(要实现这个的话记一个反向边就行)。加边的时候用的是启发式合并,并查集内部则用路径压缩,还要用一个栈记录操作过的xi,xj,做完一组数据后还原。这样时间复杂度为O(nlog(n))

CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=100100;struct edge{    int obj;    edge *Next,*rev;} e[maxn<<1];edge *head[maxn];int cur=-1;int fa[maxn];int Size[maxn];int sak[maxn<<1];int tail=0;int ans[maxn];int num=0;int n;void Up(int x){    if (x==fa[x]) return;    Up(fa[x]);    fa[x]=fa[ fa[x] ];}void Delete(){    while (tail)    {        int x=sak[tail];        head[x]=NULL;        fa[x]=x;        Size[x]=0;        tail--;    }}void Add(int x,int y){    cur++;    e[cur].obj=y;    e[cur].rev=&e[cur+1];    e[cur].Next=head[x];    head[x]=e+cur;    cur++;    e[cur].obj=x;    e[cur].rev=&e[cur-1];    e[cur].Next=head[y];    head[y]=e+cur;}int main(){    freopen("2.in","r",stdin);    freopen("2.out","w",stdout);    scanf("%d",&n);    for (int i=1; i<=n; i++) head[i]=NULL,fa[i]=i;    for (int i=1; i<=n; i++)    {        int x,y,z;        scanf("%d%d%d",&x,&y,&z);        if (x==y)            if (z) continue;            else            {                Delete();                ans[++num]=i;                continue;            }        sak[++tail]=x;        sak[++tail]=y;        Up(x);        Up(y);        if (!z)            if (fa[x]==fa[y]) ans[++num]=i,Delete();            else Add(fa[x],fa[y]),Size[ fa[x] ]++,Size[ fa[y] ]++;        else        {            if (fa[x]==fa[y]) continue;            if (Size[ fa[x] ]>Size[ fa[y] ]) swap(x,y);            edge *q=NULL;            bool sol=true;            for (edge *p=head[ fa[x] ]; p; p=p->Next)                if (p->obj==fa[y])                {                    ans[++num]=i;                    Delete();                    sol=false;                    break;                }                else p->rev->obj=fa[y],q=p;            if (!sol) continue;            if (q)            {                q->Next=head[ fa[y] ];                head[ fa[y] ]=head[ fa[x] ];            }            Size[ fa[y] ]+=Size[ fa[x] ];            fa[ fa[x] ]=fa[y];        }    }    printf("%d\n",num);    for (int i=1; i<=num; i++) printf("%d\n",ans[i]-ans[i-1]);    return 0;}

T3:路径交(hdu6110)

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6110

题目分析:在我做这题之前网上好像还没有人写这题的总结,所以我并不知道是否有人用比我的方法更优的解法,我的方法是O(nlog2(n))的,在这里说一下吧。
首先我们需要知道怎么用O(log(n))的时间求两条路径的交集,这里有一篇写得比较简明易懂的博客:https://blog.sengxian.com/solutions/noip-2015-day2,里面在讲NOIP2015DAY2T3的时候,讲了树上路径求交的一种简便的做法(虽然常数因子巨大-_-!)。我写的代码参考了这篇博客里介绍的方法(在此感谢博主),即a->b的路径与c->d的路径求交,交出来的路径的两个端点一定是Lca(a,b),Lca(a,c),Lca(a,d),Lca(b,c),Lca(b,d),Lca(c,d)中的其中两个。我们不妨先求出这六个点,然后枚举其中两个,看一下它们两点间的路径是否在a->b,c->d的路径上(判断是否合法),是的话看一下它们间的距离是否比之前枚举的所有合法路径长,长则用这两个点之间的路径作为a->b,c->d路径的交集。那么如何知道一条路径是否被另一条路径所包含?我们只要判断这条路径的的两个端点是否在另一条路径上即可,因为树上两点间的路径是唯一的。如何判断一个点x是否在a->b的路径上呢?设c=Lca(a,b),则若x与a,b其中一个点的Lca是x,与另一个点的Lca是c,x便在这条路径上。上面这些结论的正确性证明可以自己YY一下(其实主要是我也不会太严谨的证明啦)。
然后知道了树上路径求交,剩下的事就简单了。我们发现路径交集是满足区间信息加法的,于是可以用一个ST表存储路径交。设f[i][j]为第i条路径到第i+2j条路径的交集,则维护这个表的时间是O(nlog2(n))的,以后每次查询的时间都是O(log(n))的。顺便说一下,这题如果用我的方法真的很卡常,一开始我觉得完全没有希望AC,后来加了各种玄学优化(主要是在降低倍增Lca的常数的方面下了很多功夫,因为Lca函数要被调用很多次,具体看代码吧),然后2800ms卡过……

CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=500100;const int maxl=22;typedef long long LL;struct data{    int val;    data *dNext;} cnt[maxn];struct edge{    int obj;    LL len;    edge *Next;} e[maxn<<1];edge *head[maxn];int cur=-1;int dep[maxn];LL dis[maxn];int fa[maxn][maxl];int L[maxn][maxl];int R[maxn][maxl];int tp[6];int P[maxn];int max_dep=0;int l[maxn];int r[maxn];int n,m,q;int Read(){    int x=0;    char c=getchar();    while ( c<'0' || '9'<c ) c=getchar();    while ( '0'<=c && c<='9' ) x=(x<<3)+(x<<1)+(c-'0'),c=getchar();    return x;}void Add(int x,int y,LL z){    cur++;    e[cur].obj=y;    e[cur].len=z;    e[cur].Next=head[x];    head[x]=e+cur;}int Max(int x,int y){    return ((x>y)? x:y);}void Dfs(int node){    max_dep=Max(max_dep,dep[node]);    for (edge *p=head[node]; p; p=p->Next)    {        int son=p->obj;        if (son!=fa[node][0])        {            fa[son][0]=node;            dep[son]=dep[node]+1;            dis[son]=dis[node]+p->len;            Dfs(son);        }    }}void Make_fa(){    for (int j=1; j<=P[max_dep]+1; j++)        for (int i=1; i<=n; i++)                fa[i][j]=fa[ fa[i][j-1] ][j-1];}void Swap(int &x,int &y){    int z=x;    x=y;    y=z;}int Lca(int u,int v){    if (dep[u]<dep[v]) Swap(u,v);    for (data *p=&cnt[ dep[u]-dep[v] ]; p!=cnt; p=p->dNext)        u=fa[u][p->val];    if (u==v) return u;    int mj=P[ dep[u] ];    for (int j=mj; j>=0; j--)        if ( fa[u][j]!=fa[v][j] )            u=fa[u][j],v=fa[v][j];    return fa[u][0];}bool Check(int x,int a,int b,int c){    int p=Lca(a,x);    int q=Lca(b,x);    return ( ( p==c && q==x ) || ( q==c && p==x ) );}bool Judge(int a,int b,int c,int d,int lca){    return ( Check(a,c,d,lca) && Check(b,c,d,lca) );}void Get(int a,int b,int c,int d,int &u,int &v){    tp[0]=Lca(a,b);    tp[1]=Lca(a,c);    tp[2]=Lca(a,d);    tp[3]=Lca(b,c);    tp[4]=Lca(b,d);    tp[5]=Lca(c,d);    int now_len=0;    for (int i=0; i<5; i++) if (tp[i])        for (int j=i+1; j<6; j++) if (tp[j])        {            int k=Lca(tp[i],tp[j]);            LL temp=dep[ tp[i] ]+dep[ tp[j] ]-2LL*dep[k];            if (temp<=now_len) continue;            bool f=Judge(tp[i],tp[j],a,b,tp[0]);            if (!f) continue;            f&=Judge(tp[i],tp[j],c,d,tp[5]);            if (f) u=tp[i],v=tp[j],now_len=temp;        }}int main(){    freopen("3.in","r",stdin);    freopen("3.out","w",stdout);    P[0]=-1;    for (int i=1; i<maxn; i++)    {        P[i]=P[i>>1]+1;        int x=(i&(-i));        cnt[i].dNext=&cnt[i-x];        while (x) cnt[i].val++,x>>=1;        cnt[i].val--;    }    n=Read();    for (int i=1; i<=n; i++) head[i]=NULL;    for (int i=1; i<n; i++)    {        int x=Read(),y=Read(),z=Read();        Add(x,y,z);        Add(y,x,z);    }    fa[1][0]=1;    Dfs(1);    Make_fa();    m=Read();    for (int i=1; i<=m; i++) l[i]=Read(),r[i]=Read();    for (int i=1; i<m; i++)        Get(l[i],r[i],l[i+1],r[i+1],L[i][0],R[i][0]);    for (int j=1; j<=P[m]; j++)        for (int i=1; i<=m; i++)            if ( i+(1<<j)<=m )            {                int mid=i+(1<<(j-1));                int a=L[i][j-1];                int b=R[i][j-1];                int c=L[mid][j-1];                int d=R[mid][j-1];                if ( a && b && c && d )                    Get(a,b,c,d,L[i][j],R[i][j]);            }    q=Read();    for (int i=1; i<=q; i++)    {        int x=Read(),y=Read();        int u=0,v=0;        if (x==y) u=l[x],v=r[x];        else        {            int z=P[y-x];            y-=(1<<z);            if ( L[x][z] && R[x][z] && L[y][z] && R[y][z] )                Get(L[x][z],R[x][z],L[y][z],R[y][z],u,v);        }        if ( u && v )        {            int w=Lca(u,v);            LL ans=dis[u]+dis[v]-2LL*dis[w];            printf("%I64d\n",ans);        }        else printf("0\n");    }    return 0;}

T4:迷宫出逃(hdu6111)

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6111

题目分析:这题我考试的时候看完题就没去想,因为考场上没有人AC,而且当时我剩余的时间也不是很充裕。后来比赛结束之后我又重新想了想,感觉只会宽搜的做法,而且还要O(n2)的时间(直接暴力判重),如果答案步数很多的话可能会超时。后来上网看了看某神犇写的题解,发现真的就是用BFS做的,只不过判重用了Hash表,好像BFS出来的状态数不会超过10000个=_=!!!……简单来说就是将每个格子可不可以到达用一个unsigned long long存起来,再存一个当前坐标和一个代表是否拿到钥匙的bool类型,搜一遍即可。

CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=70;const int M=100007;const long long M1=998353244;const long long M2=1000000007;const long long M3=133333331;const int u[4]={0,1,0,-1};const int v[4]={1,0,-1,0};typedef unsigned long long ULL;struct data{    ULL map;    bool key;    int X,Y;} Hash[M];data que[M];int step[M];int head,tail;int a[maxn][maxn];char s[maxn];int sx,sy,kx,ky,ex,ey;int t,n,m,ans;void Push(int x){    ULL tp=que[x].map%(unsigned long long)M;    long long y=tp;    y=(y+(long long)que[x].key*M1)%M;    y=(y+(long long)que[x].X*M2)%M;    y=(y+(long long)que[x].Y*M3)%M;    while ( Hash[y].X!=-1 )    {        y++;        if (y==M) y=0;    }    Hash[y]=que[x];}ULL Get(int x,int y){    x=x*m+y;    x=n*m-x-1;    ULL tp=1;    tp<<=x;    return tp;}bool Check(data x){    ULL tp=x.map%(unsigned long long)M;    long long y=tp;    y=(y+(long long)x.key*M1)%M;    y=(y+(long long)x.X*M2)%M;    y=(y+(long long)x.Y*M3)%M;    while ( Hash[y].X!=-1 &&            ( Hash[y].map!=x.map || Hash[y].key!=x.key ||              Hash[y].X!=x.X || Hash[y].Y!=x.Y ) )    {        y++;        if (y==M) y=0;    }    if (Hash[y].X==-1) return false;    return true;}void Bfs(){    for (int i=0; i<M; i++) Hash[i].X=-1;    head=0,tail=1;    que[1].map=0;    for (int i=0; i<n; i++)        for (int j=0; j<m; j++)        {            int x=a[i][j];            if (x==2) x=0;            que[1].map=( (que[1].map<<1)|x );        }    que[1].key=0;    que[1].X=sx;    que[1].Y=sy;    Push(1);    while (head<tail)    {        head++;        int x=que[head].X,y=que[head].Y;        for (int i=0; i<4; i++)        {            int dx=x+u[i],dy=y+v[i];            if ( dx<0 || n<=dx || dy<0 || m<=dy ) continue;            if ( !( que[head].map & Get(dx,dy) ) )            {                if ( dx==kx && dy==ky )                {                    data p;                    p.map=que[head].map;                    p.key=1;                    p.X=dx;                    p.Y=dy;                    if ( !Check(p) )                        que[++tail]=p,step[tail]=step[head]+1,                        Push(tail);                }                if ( dx==ex && dy==ey && que[head].key )                {                    ans=step[head]+1;                    return;                }                if ( a[dx][dy]==2 )                {                    data p;                    p.map=que[head].map;                    if (dx) p.map^=Get(dx-1,dy);                    if (dy) p.map^=Get(dx,dy-1);                    if (dx<n-1) p.map^=Get(dx+1,dy);                    if (dy<m-1) p.map^=Get(dx,dy+1);                    p.key=que[head].key;                    p.X=dx;                    p.Y=dy;                    if ( !Check(p) )                        que[++tail]=p,step[tail]=step[head]+1,                        Push(tail);                }                if ( a[dx][dy]!=2 )                {                    data p;                    p.map=que[head].map;                    p.key=que[head].key;                    p.X=dx;                    p.Y=dy;                    if ( !Check(p) )                        que[++tail]=p,step[tail]=step[head]+1,                        Push(tail);                }            }        }    }}int main(){    freopen("4.in","r",stdin);    freopen("4.out","w",stdout);    scanf("%d",&t);    for (int g=1; g<=t; g++)    {        printf("Case #%d:\n",g);        scanf("%d%d",&n,&m);        ans=-1;        for (int i=0; i<n; i++)        {            scanf("%s",&s);            for (int j=0; j<m; j++)            {                if (s[j]=='x') a[i][j]=1;                else                    if (s[j]=='*') a[i][j]=2;                    else                    {                        if (s[j]=='S') sx=i,sy=j;                        if (s[j]=='K') kx=i,ky=j;                        if (s[j]=='E') ex=i,ey=j;                        a[i][j]=0;                    }            }        }        Bfs();        printf("%d\n",ans);    }    return 0;}

T5:今夕何夕(hdu6112)

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6112

题目分析:这应该是本场比赛最没有技术含量的一题,也是最多WA的一题。总的来说就是看输入的日期和2月29号的关系,如果在2月29号之前,则到下一年的同一天为止经过的天数由今年是不是闰年决定;在2.29之后则要看下一年是不是闰年;如果刚好是2.29则找到下一个闰年,看看中间经过了多少天。

CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;int t,y,m,d;bool Judge(int x){    if ((x%100)==0) return ((x%400)==0);    return ((x%4)==0);}int Get(int x){    if ( Judge(x) ) return (366%7);    return (365%7);}int main(){    freopen("1005.in","r",stdin);    freopen("1005.out","w",stdout);    scanf("%d",&t);    while (t--)    {        scanf("%d-%d-%d",&y,&m,&d);        int k=0;        if ( m<2 || ( m==2 && d<29 ) )        {            k=(k+Get(y))%7;            y++;            while (k)            {                k=(k+Get(y))%7;                y++;            }            printf("%d\n",y);        }        else            if ( m==2 && d==29 )            {                if ( Judge(y+4) ) k=(k+1461)%7;                else k=(k+1460)%7;                y+=4;                while ( k || (!Judge(y)) )                {                    if ( Judge(y+4) ) k=(k+1461)%7;                    else k=(k+1460)%7;                    y+=4;                }                printf("%d\n",y);            }            else            {                y++;                k=(k+Get(y))%7;                while (k)                {                    y++;                    k=(k+Get(y))%7;                }                printf("%d\n",y);            }    }    return 0;}

T6:度度熊的01世界(hdu6113)

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6113

题目分析:这题一开始以为很难,后来发现就是个搜索……我们先深搜一遍看看1是不是只组成了1个连通块。然后搜一遍所有0组成的连通块,如果它碰到了地图的边界,那么它就不被1所完全包含,否则它被1组成的连通块完全包含。最后看看有几个被1包含的0组成的连通块即可,还要注意一下没有1的情况。

CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=110;const int u[4]={0,1,0,-1};const int v[4]={1,0,-1,0};bool f[maxn][maxn];bool vis[maxn][maxn];char s[maxn];int num1;int n,m;bool Dfs(int x,int y,int z){    bool fl=true;    if ( x==1 || x==n || y==1 || y==m ) fl=false;    if (f[x][y]) num1--;    vis[x][y]=true;    for (int i=0; i<=3; i++)    {        int dx=x+u[i],dy=y+v[i];        if ( 1<=dx && dx<=n && 1<=dy && dy<=m && f[dx][dy]==z && !vis[dx][dy] )            fl&=Dfs(dx,dy,z);    }    return fl;}int main(){    freopen("1006.in","r",stdin);    freopen("1006.out","w",stdout);    scanf("%d%d",&n,&m);    while ( n && m )    {        num1=0;        for (int i=1; i<=n; i++)        {            scanf("%s",&s);            for (int j=1; j<=m; j++)            {                f[i][j]=s[j-1]-'0';                if (f[i][j]) num1++;            }        }        memset(vis,false,sizeof(vis));        int yy=0;        for (int i=1; i<=n; i++)            for (int j=1; j<=m; j++)                if ( !vis[i][j] && f[i][j] )                {                    Dfs(i,j,1);                    yy++;                }        if (yy!=1) printf("-1\n");        else        {            int x=0;            for (int i=1; i<=n; i++)                for (int j=1; j<=m; j++)                    if ( !vis[i][j] )                    {                        bool k=Dfs(i,j,0);                        if (k) x++;                    }            if (!x) printf("1\n");            else                if (x==1) printf("0\n");                else printf("-1\n");        }        n=0,m=0;        scanf("%d%d",&n,&m);    }    return 0;}

总结:这次比赛考的真的很差,完全就是发挥失常。比赛开始10分钟,我浏览了一遍所有题目,先切T5。由于越快AC得分越高,我一改往常的写代码风格,用前所未有的手速敲出T5的code,然后错漏百出,有很多细节没有考虑。我写的程序不停地WA,调来调去调不过,然后我去看了看ranklist,发现好多人AC了两题了!我心里越来越慌,更加发现不了程序的错,后来甚至找了机房里和我一起考比赛的tututu的代码来对拍才发现错。由于评测姬那蜗牛般的速度,我看到自己T5显示AC的时候比赛已经过去了75min……现在想来当时实在是太紧张了,导致代码写错反而浪费更多时间。
然后用5min写了T1大水题的code(然而显示AC又过了15min)。写了一发T6,15:50交了程序,然后想T2,T3,T4,发现一题都不会。4:20的时候我又看了看我T6的code,发现没有特判地图中没有1的情况,无奈又交了一发(此时我15:50交的代码还没开始评测)。比赛一结束不知为何评测姬就跑得飞快了,然后我看到我T6WA了……我赶紧看看代码,发现本应如果没有被1包含的0的连通块输出1,有一个则输出0,而我将它们搞反了……其实本来这是连样例都不过的,但我当时心态爆炸,见到样例输出一个1,一个0,一个-1就直接交了……由于只AC了两题,而且T5WA了很多次,直接rk1200多。幸好第二天的初赛B吸取了教训稳定了心态,回到了rk120多,不然连复赛都进不了了QAQ。以后参加这种重大的ACM赛制的比赛一定要稳中求进,不要贪快,平时也要锻炼自己快速写对code的能力。还有ranklist只是用来参考下一步该做哪题,绝不能因为看见别人AC了这么多题就乱了自己的节奏。

阅读全文
1 0