机房水题欢乐赛 2016-02-02

——暨GDKOI校队选拔赛

T1: 简单题

给出N个整数 X1,X2,X3,,Xn ,将这 N 个数从小到大排序为A1,A2,A3,,An,记数列 X1,X2,X3,,Xn 的奇数项之和为 P ,偶数项之和为Q,令 T=PQ ,求出 T 的值.

输入格式

输入文件的第1行为整数N(1≤N≤50000).
接下来的N行每行有一个整数,按顺序给出X1,X2,X3,…,Xn的值(|Xi|≤1000)

输出格式

给出整数T的值.

输入

3
1
3
2

输出

2

题解

居然有人理解成为偶数的项和与为奇数的项和的差。。。

程序

#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 50500;
int a[N];

int main() {
    int n, i, p = 0, q = 0;
    scanf("%d", &n);
    for (i = 0; i < n; i++) scanf("%d", &a[i]);
    sort(a, a + n);
    for (i = 0; i < n; i += 2) p += a[i];
    for (i = 1; i < n; i += 2) q += a[i];
    if (p < q) swap(p, q);
    printf("%lld", p - q);
    return 0;
}

T2: 电路稳定性

zzz有一个电路,电路上有n个元件。已知元件i损坏而断开的概率是Pi(i=1,2,…,n,0≤Pi≤1)。
请你帮zzz算出整个电路断路的概率。
元件的连接方式很简单,对电路的表示:
1. 一个元件是最小的电路,用A表示元件1,B表示元件2,如此类推。
2. k个电路组成的串联电路表示为:电路1,电路2,…,电路k。
k个电路组成的并联电路表示为:(电路1)(电路2)…(电路k)。

Input

第一行是一个整数n(1≤n≤26),表示一共有多少个元件;第二行是表示电路的字符串;最后是n行,每行是一个实数Pi(i=1,…n,0≤Pi≤1),表示该元件断路的概率。

Output

输出一个实数,表示整个电路断路的概率,精确到小数点后4位。

Sample Input

5(A,B)((C)(D),E)0.20.30.40.50.6

Sample Output

0.2992

题解

并联的概率是P1P2Pn
串联的概率是 1(1P1)(1P2)(1Pn) ,即1-串联不断路的概率。
至于字符串处理,递归也行,栈处理括号也行。

程序

#include <cstdio>
#include <cstring>
#include <list>
using namespace std;
const int N = 1000;
char c[N];
double stk[N], p[32];
list<double> s[N];

int main() {
    int n, i, l, top = 0;
    scanf("%d%s", &n, c);
    stk[0] = 1.0;
    for (i = 0; i < n; i++) scanf("%lf", p + i);
    l = strlen(c);
    for (i = 0; i < l; i++) {
        if (c[i] == '(')
            ++top, stk[top] = 1.0;
        else if (c[i] == ')') {
            double tmp = 1.0 - stk[top];
            for (list<double>::iterator it = s[top].begin();
                 it != s[top].end(); it ++)
                tmp *= 1.0 - *it;
            s[top].clear();
            --top, stk[top] *= 1.0 - tmp;
        } else if (c[i] == ',')
            s[top].push_back(stk[top]), stk[top] = 1;
        else
            stk[top] *= p[c[i] - 'A'];
    }
    printf("%.4lf", stk[0]);

    return 0;
}

T3

描述

Lordaeron市的我地图是一个n*n的矩阵,其中标号为1的表示商业区,标号为0的表示居民区。为了考察市内居民区与商业区的距离,并对此做出评估,市长Arthas希望你能够编写一个程序完成这一任务。
居民区i到商业区的距离指的是到距离它最近的商业区j的距离( XiXj+YiYj ),而你将统计的是对于城市中的每一个区域 k ,以它为中心,所有满足max{|XkXm|,|YkYm|}r的区域 m 到商业区的距离之和。结果同样以nn的矩阵形式给出。

输入格式

输入文件的第1行为整数 T(1T30) ,表示输入数据的组数。
每组数据的第1行为 n,r(1r<n150) ,第2行起为一个 nn 的矩阵。

输出格式

对每个输入数据,输出一个 nn 的矩阵。
输出矩阵间有空行

输入样例

1
4 1
1 0 0 0
1 1 0 0
0 1 1 0
0 1 0 0
0 1 1 0

输出样例

1 4 9 8
2 5 10 9
2 4 7 7
2 3 4 4

题解

考试时样例多了最后一行。。。其实忽略它就行了。
而且没告诉输出数据间有空行。。。
首先如何快速求i距离最近的商业区的曼哈顿距离,发现实际上如果确定了曼哈顿距离,那么商业区所处的位置就围成了一个旋转了45°的正方形,显然我们可以二分这个距离,问题是使用前缀和判断有无商业区在旋转了45°的正方形不方便,我们可以将坐标系倒过来。
x=xy,y=x+y ,自己画一下图就知道结果如何了。
于是用两次前缀和即可。复杂度 O(n2logn)
我的Region的写法比较坑。。

程序

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1000;
int read() {
    int s = 0, f = 1; char ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') f = -1;
    for (; '0' <= ch && ch <= '9'; ch = getchar()) s = s * 10 + ch - '0';
    return s * f;
}

struct Array {
    int c[N][N];

    int &get(int x, int y) { return c[x + 500][y + 500]; }
    int *operator [](int x) { return c[x]; }

    void clear() {
        memset(c, 0, sizeof c);
    }
};

struct Region {
    Array c, s;

    int &get(int x, int y) { return c.get(x, y); }

    void process() {
        int i, j;
        for (i = 0; i < N; i++) s[i][0] = s[0][i] = 0;
        for (i = 1; i < N; i++)
            for (j = 1; j < N; j++)
                s[i][j] = s[i][j - 1] + s[i - 1][j] - s[i - 1][j - 1] + c[i][j];
    }

    int query(int x1, int y1, int x2, int y2) {
        if (x1 > x2) swap(x1, x2);
        if (y1 > y2) swap(y1, y2);
        x1--; y1--;

        return s.get(x2, y2) - s.get(x1, y2) - s.get(x2, y1) + s.get(x1, y1);
    }

    void init() {
        c.clear(); s.clear();
    }
} rg_rotated, rg_new;

int main() {
    int t = read(), n, m, i, j, l, r, x, y, mid, ans;
    while (t--) {
        n = read(); m = read();
        rg_rotated.init();
        rg_new.init();
        for (i = 1; i <= n; i++)
            for (j = 1; j <= n; j++)
                rg_rotated.get(i - j, i + j) = read();
        rg_rotated.process();
        for (i = 1; i <= n; i++)
            for (j = 1; j <= n; j++) {
                x = i - j; y = i + j;
                l = 0; r = n;
                while (l <= r) {
                    mid = l + r >> 1;
                    if (rg_rotated.query(x - mid, y - mid, x + mid, y + mid))
                        r = mid - 1, ans = mid;
                    else l = mid + 1;
                }
                rg_new.get(i, j) = ans;
            }
        rg_new.process();
        for (i = 1; i <= n; i++) {
            for (j = 1; j <= n; j++)
                printf("%d ", rg_new.query(i - m, j - m, i + m, j + m));
            printf("\n");
        }
        printf("\n");
    }

    return 0;
}

T4: 烟花的寿命

(http://cojs.tk/cogs/problem/problem.php?pid=430)
然而oj上没有自定义校验器

描述

见过夜空中美丽的烟花吗,它们总是由一个块炸成多块,可能还会继续分裂成更小的块。然而,并不是每块都会继续发光。如果把一个烟花炸开的整个过程中的亮点记录下来,并给所有爆炸点标上号,你能算出它最长可能的存活时间吗?(指第一次爆炸到最后一次之间间隔的时问,假设任意两次相邻的爆炸时间间隔都是1秒)。

输入

(输入文件名fireworlk.in)
第1行一个数T,说明输入文件中共T组数据。每组的第l行是爆炸的总数N(1

输出

(输出文件名fireworlk.out)
对每组输入,输出若干行。第1行是最长的时间x(秒),接着输出x+1个数,每数占一行,给出最长时间是怎样达到的(从哪个点开始,经过哪些点)。如果存在多解,则任意输出一组解。

输入(firework.in)

2
3
1 2
1 3
4
1 2
2 4
3 2

输出(firework.out)

2
2
1
3
2
1
2
4
图为样例中第2组输出的示意图。
烟花的寿命

题解

神TM考官要我写自定义校验器。
发现就是求树的最长链。
一个dp x dfs搞定。
对于每个点,过其的链有两种情况,一种是从其父亲延续下来的,一种是过该节点,分别向两个孩子走的。
所以:dfs返回的是从该点到叶节点的最长距离。

func dfs(x, father, father_distance) {
    child = []
    for (v in adj[x] - father)
        child += dfs(v, x, father_distance + 1)
    ans = max{ans, child.min + father_distance + 1, child.min + child.second_min + 2}
    return child.min
}

输出路径方案感觉是这道题最麻烦的地方?我的代码写的好丑。。

#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
int read() {
    int s = 0, f = 1; char ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') f = -1;
    for (; '0' <= ch && ch <= '9'; ch = getchar()) s = s * 10 + ch - '0';
    return s * f;
}
const int N = 1050, M = 2050, inf = 1000000000;
int h[N], v[M], p[M], cnt = 0, child[N], father[N];
void add(int x, int y) {
    p[++cnt] = h[x]; v[cnt] = y; h[x] = cnt;
}
int ans, ans_rt, ans_fa, ans_c1, ans_c2;
int dp(int x, int fa, int fa_dis) {
    int ma1 = -inf, ma1c = -1, ma2 = -inf, ma2c = -1, c;
    for (int i = h[x]; i; i = p[i])
        if (v[i] != fa) {
            c = dp(v[i], x, fa_dis + 1);
            if (c > ma1) ma2c = ma1c, ma2 = ma1, ma1 = c, ma1c = v[i];
            else if (c > ma2) ma2 = c, ma2c = v[i];
        }
    if (ma1 + fa_dis + 1 > ans) {
        ans = ma1 + fa_dis + 1; ans_rt = x;
        ans_fa = 1; ans_c1 = ma1c; ans_c2 = -1;
    }
    if (ma1 + ma2 + 2 > ans) {
        ans = ma1 + ma2 + 2; ans_rt = x;
        ans_fa = -1; ans_c1 = ma1c; ans_c2 = ma2c;
    }
    father[x] = fa;
    child[x] = ma1c;
    return ma1c == -1 ? 0 : (ma1 + 1);
}

vector<int> output;

int main() {
    int t = read(), n, i, x, y, j;
    while (t--) {
        n = read(); cnt = ans = 0;
        memset(h, 0, sizeof h);
        for (i = 1; i < n; i++) {
            x = read(); y = read();
            add(x, y); add(y, x);
        }
        ans_fa = ans_c1 = ans_c2 = -1;
        dp(1, 0, 0);
        printf("%d\n", ans);
        if (ans_fa != -1) {
            output.clear();
            for (j = ans_rt; j != 0; j = father[j])
                output.push_back(j);
            reverse(output.begin(), output.end());
            for (j = 0; j < output.size(); j++)
                printf("%d\n", output[j]);
        }
        if (ans_c2 != -1) {
            output.clear();
            for (j = ans_c2; j != -1; j = child[j])
                output.push_back(j);
            reverse(output.begin(), output.end());
            for (j = 0; j < output.size(); j++)
                printf("%d\n", output[j]);
            printf("%d\n", ans_rt);
        }
        if (ans_c1 != -1) {
            for (j = ans_c1; j != -1; j = child[j])
                printf("%d\n", j);
        }
    }

    return 0;
}

顺便自定义校验器(Cena 0.8.2)

#include <cstdio>
#include <string>
#include <fstream>
using namespace std;

const int N = 1050, M = 2050, inf = 1000000000;
int h[N], v[M], p[M], cnt = 0, ans;
void add(int x, int y) {
    p[++cnt] = h[x]; v[cnt] = y; h[x] = cnt;
}

int main(int argc, char **argv) {
    ifstream standard_input("firework.in");
    ifstream standard_output(argv[2]);
    ifstream player_output("firework.out");

    ofstream score("score.log");
    ofstream report("report.log");

    int t, n, i, x, y, standard_time, player_time, fa = -1, s;
    standard_input>>t;
    for (int kase = 1; kase <= t; kase++) {
        standard_input>>n; cnt = ans = 0;
        memset(h, 0, sizeof h);
        for (i = 1; i < n; i++) {
            standard_input>>x>>y;
            add(x, y); add(y, x);
        }
        standard_output>>standard_time;
        for (i = 0; i <= standard_time; i++) standard_output>>x;
        if (player_output.eof()) {
            score<<"0";
            report<<"数据组"<<kase<<"错误,选手文件意外结束"<<endl;
            return 0;
        }
        player_output>>player_time;
        if (player_time != standard_time) {
            score<<"0";
            report<<"数据组"<<kase<<"错误,标准输出时间:"<<standard_time<<",选手输出时间:"<<player_time<<endl;
            return 0;
        }
        for (i = 0; i <= standard_time; i++) {
            if (player_output.eof()) {
                score<<"0";
                report<<"数据组"<<kase<<"错误,选手文件意外结束"<<endl;
                return 0;
            }
            player_output>>s;
            if (s < 1 || s > n) {
                score<<"0";
                report<<"数据组"<<kase<<"错误,超出数据范围"<<endl;
                return 0;
            }
            if (i > 0) {
                bool flag = false;
                for (int it = h[fa]; it; it = p[it])
                    if (v[it] == s) {
                        flag = true; break;
                    }
                if (flag == false) {
                    score<<"0";
                    report<<"数据组"<<kase<<"错误,选手输出路径不存在"<<endl;
                    return 0;
                }
            }
            fa = s;
        }
    }
    score<<argv[1];

    return 0;
}

这些题目都在谷歌找到了,然而百度都找不到。
想起2016冬令营开幕式讲的话了。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值