2014年第五届福建省大学生程序设计竞赛省赛6题题解

比赛题目在:http://acm.fafu.edu.cn/problemList.php?volume=6

热身赛题fafuoj1557-1559共3题

省赛题fafuoj1563-1572共10题


第一天热身赛

A题签到题,B题我觉得是用字典树模版+博弈+DFS,code4101把字典树模版敲上去,我写了3行的DFS,1Y,C题看了下感觉不太好做,之后就开始测环境,最后热身赛排第10名,福大有队1人来热身,还AK了!!!果然进过WF的学校就是不一样


第二天省赛

开场发现A题是水题,我推了推公式,给code4101敲,8分1Y

B题,感觉题意有点问题,先放着

接着看C题计算几何题,感觉能做,但思路一下想不到,也先放着

D题貌似是很水的物理题,我写了个运动学的公式让code4101敲,WA,XXW说题意有点不明确看了下Clarification,裁判给了个错误的解释,然后我们就坑在这里,全场目前没一个人AC

1小时过去了还是只有1题,但是很奇怪D题没人过,明明很水(因为裁判给了错误的解释,全场都被坑了)

B题过的人比较多,XXW确认好之前没理解的题意,我就开始慢慢算

D题code4101已经3WA了,我就上去敲B,然后逗比的PE了,2Y

此时E题过的人也比较多,我和code4101开始讨论方法,code4101的方法比我好,他上去敲,敲了一会问我会不会超时,我算了下50000的规模O(n^2)的算法,貌似确实会超时,我想了个优化,code4101继续敲,他也逗比了,PE,2Y

这时我想到C题的做法,全队讨论了一下实现的细节,继续让code4101敲,WA

XXW翻译完后面所有题目,F题感觉是状压DP,但数据规模太大,我就不知怎么做了,G题博弈,感觉SG函数不是那么好套,HI我没看,J题在树上搞一些什么东西,感觉就是解不出来的题

之后就是C题D题被卡的状态,一直WA,C题加上eps,再加三点共线特判(XXW写的,code4101代码已经写晕了),不过还是WA

这时D题题目裁判已经改了好几次了,终于开始有几队过了,我们还在讨论D题题意,XXW和code4101在讨论D题,我在想C题,不过一直没进展

最后70分钟,C题我实在不知哪里的思路错了就去听了下D题题意后,我把公式改了下让code4101敲,由于他已经晕了基本我说一行他敲一行(不知为何当时我不想上去敲),继续WA,然后再找XXW再确认下题意,我再改了下我的公式再改了下代码,终于AC

然后全队看C,我想了一组测试数据测了一下,没问题,这时真不知到怎么弄,再乱输了一组测试数据就回位置,结果这组数据出问题了!!!终于发现一个BUG,然后开始调代码,先发现XXW写的那段特判忘加fabs,结果改了还错,之后又用这组我乱输的数据发现code4101写的那段代码下标写错,提交,AC

这时还有47分钟5题排第10名稳拿银奖了,我们庆祝一会,再看了下F题和G题,看看能不能再解出一题拿金,F题G题一题DP一题博弈,队里DP和博弈我是最熟的,code4101也会,而XXW就开始数气球玩了,我看了下F题没思路就把F题交给code4101,我去想G题,但到封榜时都没思路,排第11名,全队就都进入放弃治疗状态

(省赛比较奇葩封榜只有30分钟,G题如果我思路错的话也可能不是DP)


最终排第13名获得一银,学校一共获得一银三铜 (我校历史上第一次拿银)

然后....比完我就想出G题的思路.....就只是SG函数打表找规律!!!......花个几分钟写了下提交1Y.....我的金牌啊QAQ,果然我还是太弱了

-----------------------------------------------------  华  丽  的  分  割  线 -------------------------------------------------------

热身赛 我就简单说说AB题


热身赛A题

模拟水题不解释


热身赛B题

字典树+博弈+DFS

大白书字典树模版抄完加下面DFS,用dfs(0)调用就可以

bool dfs(int u){
    for(int i=0;i<26;i++)if(Tree.ch[u][i]&&dfs(Tree.ch[u][i]))return false;
    return true;
}

正式赛 ABCDEG题

A题

签到题我就不写代码了,比赛时全场70个队就1个队没过

题意:给出三个关系式以及N, k1, k2, k3求a1, a2, a3, a4

思路:i=1~3时a[i]=N/(1+k[i]),剩下的就是a4


B题

题意:给一个数n,输出n被分成最小的集合数,要求集合里任意的X,Y,X&Y不能在同一个集合内

思路:找规律,发现每个数的二进制1的数量一样多的分到同一个集合,结果就为小于等于n的二进制1数量的最大值

#include<stdio.h>
#include<math.h>
int main(){
	int T,kase=0,n;
	scanf("%d",&T);
	while(T--){
        scanf("%d",&n);
        printf("Case #%d: %d\n",++kase,(int)ceil(log(n+2)/log(2))-1);
	}
	return 0;
}

C题

计算几何题

题意:给三个点组成一个三角形,输出把这个三角形面积平分线的最小长度

思路:过最长边与次长边画一条线,形成一个面积是原三角形一半的等腰三角形,新画的线与原三角形最长边与次长边的两个夹角相等,这条新边的长度就是答案

比赛时各种套定理,赛后发现学弟强推公式A了,以下是学弟的公式。。。

#include<stdio.h>
#include<math.h>
#include <iostream>
using namespace std;
double a,b,c,a1,a2,b1,b2,c1,c2;
double dis(double x1,double y1,double x2,double y2)
{
    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lf%lf%lf%lf%lf%lf",&a1,&a2,&b1,&b2,&c1,&c2);
        a=dis(a1,a2,b1,b2);
        b=dis(c1,c2,b1,b2);
        c=dis(a1,a2,c1,c2);
        if(a>b)swap(a,b);
        if(a>c)swap(a,c);
        printf("%.4f\n",sqrt(b*c-(b*b+c*c-a*a)/2));
    }
    return 0;
}

D题

很坑的一道水题

题意:给定人数n,记录次数m,记录时间间隔t,以及每个人的加速度,输出每次记录位移最大的人的编号,并把此人速度清0

坑点1:速度是在秒末突然变化,不是均匀变化,第0秒末开始才有速度

坑点2:记录和清0都是在秒末进行,比如:t=2时,第一次记录就是在第2秒末

坑点3:是位移最大的人的编号清0,比赛时裁判说是速度最大清0,导致这题前3个小时没一个人过

#include<stdio.h>
#include<math.h>
#include<string.h>
#include <iostream>
using namespace std;
typedef long long LL;
const int MAXN=100+5;
LL v0[MAXN],v[MAXN],a[MAXN],s[MAXN];
int n,m,t;
int main(){
    while(~scanf("%d%d%d",&n,&m,&t))
    {
        for(int i=0;i<n;i++)
        {
            cin>>v0[i];
            v[i]=v0[i];
            a[i]=t*(t-1)*v0[i]/2;
            s[i]=0;
        }
        for(int i=0;i<m;i++)
        {
            int p=0;
            for(int j=0;j<n;j++)
            {
                s[j]+=a[j]+v[j]*t;
                v[j]+=v0[j]*t;
                if(s[p]<s[j])p=j;
            }
            cout<<p+1;
            if(i!=m)cout<<' ';
            v[p]=0;
        }
        cout<<'\n';
    }
    return 0;
}

E题

贪心题

题意:告诉bilibili屏幕的长宽n*m,有q个弹幕,再告诉每个弹幕出现的时间以及弹幕长度,输出每条弹幕是在第几行出现,放不下就输出Failed!,要求任意一时刻,屏幕的任意一行最多只有一个弹幕,且新弹幕优先放在最上的空行

思路:开一个数组记录出去时间,每个弹幕进入时覆盖最上方记录的出去时间小等于新弹幕进入时间的弹幕

这个算法的时间复杂度是O(n^2),在50000的数据规模下显然过不了,要用O(nlogn)的算法,但是比赛时却AC了,赛后再提交却TLE

#include<stdio.h>
#include<math.h>
#include<string.h>
#include <iostream>
using namespace std;
const int MAXN=50000+5;
int a[MAXN],n,m,q;
int main(){
    int T,kase=0;
    scanf("%d",&T);
    while(T--)
    {
        printf("Case #%d:\n",++kase);
        scanf("%d%d%d",&n,&m,&q);
        memset(a,0,sizeof(a));
        for(int i=0;i<q;i++)
        {
            int t,l,j=0;
            scanf("%d%d",&t,&l);
            while(t<a[j]&&j<n)j++;
            if(j==n){printf("Failed!\n");}
            else{
                a[j]=t+l+m;
                printf("%d\n",j+1);
            }
        }
    }
    return 0;
}
据说农大oj的服务器比较慢,比赛时是另外搭的服务器,所以可能是这个原因导致上面那段代码比赛时能过,赛后不能过(其实比赛能过我都感觉很奇葩)

下面这段代码是我赛后另外写的时间复杂度为O(nlogn)的代码,思路照着注释看吧

#include<stdio.h>
#include<math.h>
#include<string.h>
#include <stdlib.h>
#include<set>
#include <iostream>
using namespace std;
typedef long long LL;
struct Point{
    int t,id;//t是弹幕出去的时间,id是这条弹幕的行编号
    bool operator < (const Point& x)const{
        if(t!=x.t)return t<x.t;
        else return id<x.id;
    }
}p;
set<int>s;//现在屏幕无弹幕的行
set<Point>now;//现在屏幕内的弹幕
int n,m,q;
int main(){
#ifdef DEBUG
   freopen("CBin.txt","r",stdin);
   //freopen("CBout.txt","w",stdout);
#endif
    int T,kase=0;
    scanf("%d",&T);
    while(T--)
    {
        printf("Case #%d:\n",++kase);
        scanf("%d%d%d",&n,&m,&q);
        s.clear();
        now.clear();
        int count=1;//count-1为已使用的屏幕行数
        int i;
        for(i=0;i<q;i++)
        {
            int t,l,j=0;
            scanf("%d%d",&t,&l);
            set<Point>::iterator it=now.begin();
            while(it!=now.end()){//把在此条弹幕出现的时刻,屏幕内滚出去的弹幕放到s,并把这条弹幕加入now
                if((*it).t<=t){
                    s.insert((*it).id);
                    set<Point>::iterator itq=it;//stl我用得不太熟,这个纯粹是为了删数据建的变量,不知有没更好的方法
                    it++;
                    now.erase(itq);
                }else break;
            }
            if(s.empty())//屏幕的弹幕全满就开启一条新的行放弹幕,直到使用到n+1行则跳出
            {
                if(count==n+1){printf("Failed!\n");continue;}
                p.t=t+l+m;
                p.id=count;
                now.insert(p);
                printf("%d\n",count);
                count++;
            }else{
                set<int>::iterator it2=s.begin();
                p.t=t+l+m;
                p.id=*it2;
                now.insert(p);
                printf("%d\n",*it2);
                s.erase(*it2);
            }
        }
    }
    return 0;
}

G题

博弈题

题意:N堆石子,每次取的石子数要与现在的石子数互质

思路:SG函数打表找规律,发现偶数sg值为0,1的sg值为1,其余的数第几大质数sg值就为几,非质数sg值为最小质数的编号

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 1000010;
int sg[MAXN];

void init()
{
    memset(sg,-1,sizeof(sg));
    sg[1]=1;
    int p=2;
    for(int i=0;i<MAXN;i++)
    {
        if(!(i&1))sg[i]=0;
        else if(~sg[i])continue;
        else
        {
            sg[i]=p;
            for(int j=i;j<MAXN;j+=i)
            {
                if(!~sg[j])sg[j]=p;
            }
            p++;
        }
    }
}
int main() {
    init();
    int T,kase=0,n;
    cin>>T;
    while(T--){
        kase++;
        cin>>n;
        int sum=0;
        int x;
        for(int i=0;i<n;i++)
        {
            cin>>x;
            sum^=sg[x];
        }
        if(sum)printf("Case #%d: Cdfpysw\n",kase);
        else printf("Case #%d: Nanaya\n",kase);
    }
    return 0;
}


你可能感兴趣的:(2014年第五届福建省大学生程序设计竞赛省赛6题题解)