2014 编程之美初赛第一场(大数据) 解题报告

题目1 : 焦距

时间限制: 2000ms
单点时限: 1000ms
内存限制: 256MB

描述

一般来说,我们采用针孔相机模型,也就是认为它用到的是小孔成像原理。

在相机坐标系下,一般来说,我们用到的单位长度,不是“米”这样的国际单位,而是相邻像素的长度。而焦距在相机坐标系中的大小,是在图像处理领域的一个非常重要的物理量。

假设我们已经根据相机参数,得到镜头的物理焦距大小(focal length),和相机胶片的宽度(CCD width),以及照片的横向分辨率(image width),则具体计算公式为:

Focal length in pixels = (image width in pixels) * (focal length on earth) / (CCD width on earth)

比如说对于Canon PowerShot S100, 带入公式得

Focal length in pixels = 1600 pixels * 5.4mm / 5.27mm = 1639.49 pixels

现在,请您写一段通用的程序,来求解焦距在相机坐标系中的大小。


输入

多组测试数据。首先是一个正整数T,表示测试数据的组数。

每组测试数据占一行,分别为

镜头的物理焦距大小(focal length on earth)

相机胶片的宽度(CCD width on earth)

照片的横向分辨率大小(image width in pixels),单位为px。

之间用一个空格分隔。


输出

每组数据输出一行,格式为“Case X: Ypx”。 X为测试数据的编号,从1开始;Y为焦距在相机坐标系中的大小(focallength in pixels),保留小数点后2位有效数字,四舍五入取整。


数据范围

对于小数据:focal length on earth和CCD width on earth单位都是毫米(mm)

对于大数据:长度单位还可能为米(m), 分米(dm), 厘米(cm), 毫米(mm), 微米(um),纳米(nm)



样例输入
2
5.4mm 5.27mm 1600px
5400um 0.00527m 1600px
样例输出
Case 1: 1639.47px
Case 2: 1639.47px

    解题报告:第一题不难,判断好单位就行了。代码如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <string>
#include <map>
#include <algorithm>
using namespace std;

typedef long long LL;
map<string, double> mm;

double getNum(string a)
{
    int len = a.length();

    double res=0;
    double t=1;
    bool flag=false;
    for(int i=0;i<len;i++)
    {
        if(flag) t=t*10;
        if(a[i]=='.')
        {
            flag=true;
        }
        else
        {
            res=res*10+(a[i]-'0');
        }
    }
    return res/t;
}

int cas=1;
void work()
{
    string a, b;
    double d;
    char ch;

    cin>>a>>b;
    scanf("%lf%c%c", &d, &ch, &ch);

    string da, db;
    int len = a.length();

    if(len>=2 && a[len-2]>='0' && a[len-2]<='9')
        da = a.substr(len-1, 1), a=a.substr(0, len-1);
    else
        da = a.substr(len-2, 2), a=a.substr(0, len-2);

    len = b.length();
    if(len>=2 && b[len-2]>='0' && b[len-2]<='9')
        db = b.substr(len-1, 1), b=b.substr(0, len-1);
    else
        db = b.substr(len-2, 2), b=b.substr(0, len-2);

    double res = (mm[da]*getNum(a))/(mm[db]*getNum(b))*d;

    printf("Case %d: %.2lfpx\n", cas++, res);
}

int main()
{
    mm["m"] = 1000;
    mm["dm"] = 100;
    mm["cm"] = 10;
    mm["mm"] = 1;
    mm["um"] = 0.001;
    mm["nm"] = 0.000001;

    int T;
    scanf("%d", &T);
    while(T--)
        work();
}


题目2 : 树

时间限制: 4000ms
单点时限: 2000ms
内存限制: 256MB

描述

有一个N个节点的树,其中点1是根。初始点权值都是0。

一个节点的深度定义为其父节点的深度+1,。特别的,根节点的深度定义为1。

现在需要支持一系列以下操作:给节点u的子树中,深度在l和r之间的节点的权值(这里的深度依然从整个树的根节点开始计算),都加上一个数delta。

问完成所有操作后,各节点的权值是多少。


为了减少巨大输出带来的开销,假设完成所有操作后,各节点的权值是answer[1..N],请你按照如下方式计算出一个Hash值(请选择合适的数据类型,注意避免溢出的情况)。最终只需要输出这个Hash值即可。


MOD =1000000007; // 10^9 + 7

MAGIC= 12347;

Hash =0;

For i= 1 to N do

   Hash = (Hash * MAGIC + answer[i]) mod MOD;

EndFor


输入

第一行一个整数T (1 ≤ T ≤ 5),表示数据组数。

接下来是T组输入数据,测试数据之间没有空行。

每组数据格式如下:

第一行一个整数N (1 ≤ N ≤ 105),表示树的节点总数。

接下来N - 1行,每行1个数,a (1 ≤ a ≤ N),依次表示2..N节点的父亲节点的编号。

接下来一个整数Q(1 ≤ Q ≤ 105),表示操作总数。

接下来Q行,每行4个整数,u, l, r, delta (1 ≤ u ≤ N, 1 ≤ l ≤ r ≤ N, -109 ≤ delta ≤ 109),代表一次操作。


输出

对每组数据,先输出一行“Case x: ”,x表示是第几组数据,然后接这组数据答案的Hash值。


数据范围


小数据:1 ≤ N, Q ≤ 1000

大数据:1 ≤ N, Q ≤ 105


样例解释

点1的子树中有1,2,3三个节点。其中深度在2-3之间的是点2和点3。

点2的子树中有2,3两个节点。其中没有深度为1的节点。

所以,执行完所有操作之后,只有2,3两点的权值增加了1。即答案是0 1 1。再计算对应的Hash值即可。




样例输入
1
3
1
2
2
1 2 3 1
2 1 1 1
样例输出
Case 1: 12348
解题报告:第二题。按道理说暴力是过不了的,可以我暴力也过了……贴个代码。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long LL;

const int mod=1000000007;
const int maxn = 111111;

int first[maxn], vv[maxn], nxt[maxn];
LL answer[maxn];

int dl[maxn], dr[maxn];
int l, r, d;

int cal(int n)
{
    LL h=0;
    for(int i=1;i<=n;i++)
        h = (h*12347+answer[i])%mod;
    return h;
}

void change(int n)
{
    if(dl[n]>=l && dl[n]<=r)
        answer[n] += d;

    for(int e=first[n];e;e=nxt[e])
        change(vv[e]);
}

int dfs(int n, int dep)
{
    dl[n]=dep;
    int md = dep;
    for(int e=first[n];e;e=nxt[e])
        md = max(dfs(vv[e], dep+1), md);
    return dr[n]=md;
}

int cas=0;
void work()
{
    memset(first, 0, sizeof(first));
    memset(answer, 0, sizeof(answer));

    int n;
    scanf("%d", &n);
    int e=2;

    for(int i=2;i<=n;i++)
    {
        int tmp;
        scanf("%d", &tmp);

        nxt[e] = first[tmp], vv[e]=i, first[tmp]=e++;
    }

    dfs(1, 1);

    int q;
    scanf("%d", &q);
    while(q--)
    {
        int u;
        scanf("%d%d%d%d", &u, &l, &r, &d);

        if(l>dr[u] || r<dl[u]) continue;
        change(u);
    }
    printf("Case %d: %d\n", ++cas, cal(n));
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
        work();
}


题目3 : 活动中心

时间限制: 12000ms
单点时限: 6000ms
内存限制: 256MB

描述

A市是一个高度规划的城市,但是科技高端发达的地方,居民们也不能忘记运动和锻炼,因此城市规划局在设计A市的时候也要考虑为居民们建造一个活动中心,方便居住在A市的居民们能随时开展运动,锻炼强健的身心。

城市规划局希望活动中心的位置满足以下条件:

1. 到所有居住地的总距离最小。

2. 为了方便活动中心的资源补给和其他器材的维护,活动中心必须建设在A市的主干道上。


为了简化问题,我们将A市摆在二维平面上,城市的主干道看作直角坐标系平的X轴,城市中所有的居住地都可以看成二维平面上的一个点。

现在,A市的城市规划局希望知道活动中心建在哪儿最好。


输入

第一行包括一个数T,表示数据的组数。

接下来包含T组数据,每组数据的第一行包括一个整数N,表示A市共有N处居住地

接下来N行表示每处居住地的坐标。


输出

对于每组数据,输出一行“Case X: Y”,其中X表示每组数据的编号(从1开始),Y表示活动中心的最优建造位置。我们建议你的输出保留Y到小数点后6位或以上,任何与标准答案的绝对误差或者相对误差在10-6以内的结果都将被视为正确。


数据范围

小数据:1 ≤ T ≤ 1000, 1 ≤ N ≤ 10

大数据:1 ≤ T ≤ 10, 1 ≤ N ≤ 105

对于所有数据,坐标值都是整数且绝对值都不超过106



样例解释

样例1:活动中心的最优建造位置为(1.678787, 0)



样例输入
1
3
1 1
2 2
3 3
解题报告:三分即可。中间公式做了一点点优化,时间上没有太大的区别。代码如下:
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;

typedef long long LL;

struct Node
{
    double a, b;
} node[111111];
int n;

double dis(double a, Node& b)
{
    return sqrt(a*(a-b.b)+b.a);
}

double cal(double x)
{
    double res=0;

    for(int i=0;i<n;i++)
        res += dis(x, node[i]);

    return res;
}

int cas=1;
void work()
{
    scanf("%d", &n);

    double l=-1000000;
    double r=-l;

    for(int i=0;i<n;i++)
    {
        double a, b;
        scanf("%lf%lf",&a, &b);

        node[i].a = a*a+b*b;
        node[i].b = 2*a;
    }

    while(r-l>=0.0000001)
    {
        double a = (l+r*2)/3;
        double b = (l*2+r)/3;

        double da = cal(a);
        double db = cal(b);

        if(da==db)
        {
            r=b;
            l=a;
        }
        else if(da<db)
            l=b;
        else
            r=a;
    }

    printf("Case %d: %.7lf\n", cas++, (l+r)/2);
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
        work();
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值