【2017.12.02】C组比赛总结

来源:互联网 发布:网络人身攻击怎么办 编辑:程序博客网 时间:2024/06/11 09:35

这次考得不怎么样,只有200分!
这里写图片描述

T1:读书

这题水水水!
这题就是一道循环题嘛!
直接一边循环一边做就好了!

T2:恐怖分子

这题我是直接暴力的。

这题就是求至少用多少条经过(x0,y0)的不同直线能把所有输入的点(恐怖分子)都经过。

我一开始认为这道题很难——怎么判断几个点是否在同一条直线上?

经过我的冥思苦想,终于发现,只要两个点的最简形式(即X、Y坐标都除以它们的最大公约数后的值)相等,它们就在同一条直线上。

然后就直接暴力扫一遍,找到一个还没死的恐怖分子就把他所在的直线上的所有恐怖分子杀死。最后统计一下答案就好了。

建议大家把图平移一下,使激光武器移动到(0,0)的位置(即把所有点的X坐标减去激光武器的X,Y坐标减去激光武器的Y),并一开始先把那些X为0或Y为0的恐怖分子先杀掉。

PS:Dev.C++ 5.8.3 和 Free Pascal 都有个BUG!当源文件名为gun.cpp或gun.pas时,就会运行超慢,HelloWorld也要运行个3~4秒,谁能告诉我为什么?

但我比赛时判断得不周到,错了。。。

T3:迷恋

比赛时我认为这又是找规律题了,列个表格就能找到规律,然后无脑实现代码。
我便画了一个表格,1 1 1,2 1 1,2 2 1 地写,结果花了半小时,还是没有找到规律(呵呵,2 2 1已经有63种连法了,孩纸你慢慢画吧)
后来王泓皓自己想到正解AC了,何烨成参照题解AC了。
我才知道此题要用递推!

王泓皓看题解时,发现了一下两行文字:

动态规划:
f[i][j]表示一种颜色i个,另一种颜色j个此时有多少种方法

​然后他就关闭了题解,自己找规律去了,王泓皓就画了一个类似于下图的图。
这里写图片描述
从任意一个岛屿断开这个岛链,我们就会惊奇地发现,岛屿是这样排列的(ABC分别表示三种颜色的岛屿):
A-B-C-A-B-C-A-B-C……

然后就可以发现,如果把相同颜色的岛屿分成一堆(每种颜色的一大坨中都包含了许多个那种颜色的岛屿),最后的每一种连接方式都是这样的(不一定会有那么多或只有那么多的桥哦!):
这里写图片描述
SO,最后的答案就是红蓝两色间的连接方案 × 蓝紫两色间的连接方案 × 红紫两色间的连接方案

那么怎么求两色间的连接方案呢?

设F[i][j]表示第一种颜色(在左边)有i个岛屿,第二种颜色(在右边)有j个岛屿,其中第二种岛屿中的j是刚加入的岛屿。
这里写图片描述
那很明显,F[i][j]=F[i][j-1]+某个数 了。

那加上的那个数又是多少呢?
很明显,现在我们要求的那个数是必须连接右边第j个岛屿的连接方案,也就是左边第1个岛屿连接右边第j个岛屿,第2个岛屿连接第j个岛屿,第3个岛屿连接第j个岛屿……第i个岛屿连接第j个岛屿。

但每一次我们强制让两个岛屿相连时,剩下的岛屿怎么办?由于每次强制连接的都是左边的1个岛屿和右边的1个岛屿,剩下的岛屿可连可不连,那剩下的岛屿的连接方案很显然就是F[i-1][j-1]了。由于我们可以强制连接i座岛屿,所以加上的那个数就是 F[i-1][j-1]*i 了

最后输出F[a][b]*F[a][c]*F[b][c]就好了。

PS:注意要模上 998244353 哦!这里可是有一个大坑的!所有的F[0][i]和F[i][0]全部赋值为1!

T4:送快递

这题我用DFS+贪心,结果就DIE了
正解是 二分答案 ,现在还在思考如何判断二分到的答案是否合法。。。
首先,我必须申明一下:二分不是最难办的,判断二分出来的答案是否正确才是最难办的!!!

某位同学就发现了这个规律(图中的X,Y表示一个快递员在X的位置上,另一个在Y的位置上),判断时可以用递归,从上一层某个地方连了一条线到下一层某个地方表示当上一层的那个地方成立(两个快递员间的距离小于等于二分到的答案时)时,才可以继续判断下一层的那个地方(也就是判断从X,Y出发,是否可以走到第N行,即完成):
这里写图片描述
但是,我们会发现,重复地判断一个地方是会超时滴(如第1层的S1,X1和S2,X1合法,就会都去判断X1,X2,导致判断了两次X1,X2)

怎么办呢?

开一个BZ数组,BZ[x][y]记录有没有判断过x,y明显是不行的,题目中说得清清楚楚,0≤xi≤10^9,开一个 的标记数组很明显是会爆炸的;

我们就要用X[0]和X[-1]来表示S1和S2了。

之后开一个BZ数组,BZ[x][y]记录有没有判断过X[x],X[y]行吗?
不行!题目中也有这么一句话:100000,开一个10,000,000,000大小的数组不作死吗?

那怎么办?我们再仔细看看图呗!
其实不难发现,到了第i行时,每次判断都有包含X[i],所以这一行中,只要有一个地方是合法的,那么就可以递归到X[i],x[i+1]这个位置。

我们可以开一个数组BZ(怎么又是它?!),BZ[i]表示第i行是否有递归到下一层的X[i],X[i+1](就是第i行有没有合法的)

但这样做仍会时超。。。

我们又可以发现一个神奇的规律:搜索列比搜索行更快!

而且我们的程序跑得慢还有一个原因——已经搜索到最底层了,回溯后却又继续递归!

我们可以用一个变量记录是否到达最底层,如果发现已经到达过最底层了,就返回上一层!

总结:

这次比赛还是相当有质量的,题目都挺坑。
这次还是比较粗心,下次要审清楚题提,认真思考了!

CODE

T1:

#include<cstdio>using namespace std;#define time 86400int main(){    freopen("read.in","r",stdin);    freopen("read.out","w",stdout);    int a,n,i,t;    scanf("%d%d",&n,&t);    for(i=1;i<=n;i++)    {        scanf("%d",&a);        t=t+a-time;        if(t<=0) break;    }    printf("%d\n",i);    return 0;}

T2:

#include<cstdio>#include<cmath>using namespace std;struct node{    int x,y;}a[1001],min;bool dead[1001];int s[5001];bool pd(node n1,node n2){    if(n1.x>n2.x) return 1;    if(n1.x<n2.x) return 0;    if(n1.y>n2.y) return 1;    return 0;}bool zhi(int x){    int tt=sqrt(x),i;    for(i=2;i<=tt;i++)    {        if(x%i==0) return 0;    }    return 1;}int abs(int x){    if(x<0) return 0-x;    return x;}void qsort(int l,int r){    int i=l,j=r;    node mid=a[(l+r)/2],t;    while(i<=j)    {        while(pd(mid,a[i])) i++;        while(pd(a[j],mid)) j--;        if(i<=j)        {            t=a[i];            a[i]=a[j];            a[j]=t;            i++;j--;        }    }    if(i<r) qsort(i,r);    if(l<j) qsort(l,j);}int main(){    freopen("gun.in","r",stdin);    freopen("gun.out","w",stdout);    int n,xx,yy,i,j,sum=0,ans=0;    bool b1=0,b2=0;    scanf("%d%d%d",&n,&xx,&yy);    for(i=1;i<=n;i++)    {        scanf("%d%d",&a[i].x,&a[i].y);        a[i].x-=xx,a[i].y-=yy;    }    //qsort(1,n);    for(i=2;i<=10000;i++)    {        if(zhi(i)) s[++sum]=i;    }    for(i=1;i<=n;i++)    {        //printf("%d %d\n",a[i].x,a[i].y);        if(a[i].x==0) dead[i]=b1=1;        else if(a[i].y==0) dead[i]=b2=1;    }    if(b1) ans++;    if(b2) ans++;    for(i=1;i<=n;i++) if(!dead[i])    {        dead[i]=1;ans++;        min=a[i];        for(j=1;j<=sum;j++)        {            if(zhi(min.x)&&zhi(min.y)&&min.x!=min.y) break;            if(min.x<s[j]&&min.y<s[j]) break;            while((min.x>=s[j]||min.y>=s[j])&&min.x%s[j]==0&&min.y%s[j]==0)            {                min.x=min.x/s[j];                min.y=min.y/s[j];            }        }        //printf("%d:%d\t%d\n",i,min.x,min.y);        for(j=1;j<=n;j++) if(!dead[j])        {            if(a[j].x%min.x==0&&a[j].y%min.y==0&&abs(a[j].x)>=min.x&&abs(a[j].y)>=min.y&&a[j].x/min.x==a[j].y/min.y) dead[j]=1;        }    }    printf("%d\n",ans);    return 0;}

T3:

#include<cstdio>using namespace std;#define MOD 998244353long long f[5001][5001];int main(){    freopen("love.in","r",stdin);    freopen("love.out","w",stdout);    int a,b,c,i,j;long long x,y;    scanf("%d%d%d",&a,&b,&c);    for(i=0;i<=5000;i++) f[i][0]=f[0][i]=1;    for(i=1;i<=5000;i++)    {        for(j=1;j<=5000;j++)        {            f[i][j]=(f[i][j-1]+(i*f[i-1][j-1])%MOD)%MOD;        }    }    x=(f[a][b]*f[b][c])%MOD;    y=(x*f[a][c])%MOD;    printf("%lld\n",y);    return 0;}

T4:

#include<cstdio>using namespace std;//__attribute__((optimize("-O2")))bool b[100010];int a[100010],n,max,want;int abs(int x){    if(x<0) return 0-x;    return x;}void check(int k,int i)//k<i{    if(max==n||abs(a[k]-a[i])>want) return;    if(i>max) max=i;    check(k,i+1);    if(b[i])    {        b[i]=false;        check(i,i+1);    }}int main(){    freopen("delivery.in","r",stdin);    freopen("delivery.out","w",stdout);    int ans,i,x,y,j,l=0,r,mid;    scanf("%d%d%d",&n,&x,&y);    if(x>y) r=x;    else r=y;    for(i=1;i<=n;i++)    {        scanf("%d",&a[i+1]);        if(a[i+1]>r) r=a[i+1];    }    a[0]=x,a[1]=y;    while(l<=r)    {        mid=(l+r)/2;        if(mid<abs(x-y))        {            l=mid+1;            continue;        }        for(i=0;i<=n;i++) b[i]=1;        max=-1;want=mid;        check(0,1);        if(max==n)        {            r=mid-1;            ans=mid;        }        else l=mid+1;    }    printf("%d\n",ans);    return 0;}
原创粉丝点击