GDCPC2016题解 by lby@SYSU | Asiimov

Problem A. ABCD

题目大意

给出一个四边形四条边 ABCDADBC 及两条对角线 ACBD 的长度,问这个四边形的顶点能否在一个圆上

算法思路

通过余弦定理考察 ACB ADB 是否相等即可。

时间复杂度: O(1)

代码

/**
 * Copyright © 2016 Authors. All rights reserved.
 * 
 * FileName: A.cpp
 * Author: Beiyu Li 
 * Date: 2016-05-09
 */
#include 

using namespace std;

#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)

typedef long long LL;
typedef pair<int, int> Pii;

const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;

const double eps = 1e-10;
int sgn(double x) { return x < -eps? -1: x > eps; }

inline double cosf(double a, double b, double c)
{ return (a * a + b * b - c * c) / (2 * a * b); }

int main()
{
        int T, cas = 0;
        scanf("%d", &T);

        while (T--) {
                int ab, cd, ad, bc, ac, bd;
                scanf("%d%d%d%d%d%d", &ab, &cd, &ad, &bc, &ac, &bd);
                printf("Case #%d: %s\n", ++cas, !sgn(cosf(ac, bc, ab)
                                        - cosf(ad, bd, ab))? "Yes": "No");
        }

        return 0;
}

Problem B. Bob’s magical number

题目大意

构造一个 N 个点,每个点度数都为3的无向图

算法思路

N<4 时无解
由于共有 3N2 条边,故 N 为奇数时一定无解
N 为偶数时将所有点连成环,再将对称点连起来即可

时间复杂度: O(N)

代码

/**
 * Copyright © 2016 Authors. All rights reserved.
 * 
 * FileName: B.cpp
 * Author: Beiyu Li 
 * Date: 2016-05-09
 */
#include 

using namespace std;

#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)

typedef long long LL;
typedef pair<int, int> Pii;

const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;

int main()
{
        int T, cas = 0;
        scanf("%d", &T);

        while (T--) {
                int n;
                scanf("%d", &n);
                if (n < 4 || (n & 1)) {
                        printf("Case #%d: No\n", ++cas);
                } else {
                        printf("Case #%d: Yes\n", ++cas);
                        For(i,1,n) {
                                printf("%d %d\n", i, i == n? 1: i + 1);
                                if (i <= n / 2) printf("%d %d\n", i, i + n / 2);
                        }
                }
        }

        return 0;
}

Problem C. Color

题目大意

用K种颜色对长度为N的环涂色,问考虑旋转同构下本质不同的涂色方案有多少种

算法思路

由Pólya定理可知,答案等于 i=1Nkgcd(i,N)
对于 d=gcd(i,N) kd 项共有 φ(Nd)
所以上式化简为 d|Nφ(Nd)kd

时间复杂度: O(N)

代码

/**
 * Copyright © 2016 Authors. All rights reserved.
 * 
 * FileName: C.cpp
 * Author: Beiyu Li 
 * Date: 2016-05-09
 */
#include 

using namespace std;

#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)

typedef long long LL;
typedef pair<int, int> Pii;

const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;

const int mod = 1000000007;

int n, k, ret;
vector fct;

int pow_mod(int a, int b)
{
        int ret = 1;
        for (; b; a = (LL)a * a % mod, b >>= 1)
                if (b & 1) ret = (LL)ret * a % mod;
        return ret;
}

void decomp(int n)
{
        fct.clear();
        for (int i = 2; i * i <= n; ++i) if (n % i == 0) {
                fct.push_back(Pii(i, 0));
                while (n % i) ++fct.back().second, n /= i;
        }
        if (n > 1) fct.push_back(Pii(n, 1));
}

void dfs(int i, int gcd, int phi)
{
        if (i == fct.size()) {
                ret = (ret + (LL)pow_mod(k, gcd) * phi) % mod;
                return;
        }
        dfs(i + 1, gcd, phi);
        phi *= fct[i].first - 1;
        rep(j,fct[i].second) {
                gcd /= fct[i].first;
                dfs(i + 1, gcd, phi);
                phi *= fct[i].first;
        }
}

int main()
{
        int T, cas = 0;
        scanf("%d", &T);

        while (T--) {
                scanf("%d%d", &n, &k);
                decomp(n);
                ret = 0;
                dfs(0, n, 1);
                ret = (LL)ret * pow_mod(n, mod - 2) % mod;
                printf("Case #%d: %d\n", ++cas, ret);
        }

        return 0;
}

Problem D. Dilettante

题目大意

问用 1×1×1×2 的四维块在可以旋转的情况下填充 2×2×4×n 四维空间的方案数,答案取模 1000000007

算法思路

将最后一维当作时间考虑可以发现,在某一时刻,前三维构成 2×2×4 0/1 状态立方体
对于每次转移,要么将两个相邻的0置1,要么将状态取反后到达下一时刻
我们可以先预处理构成某个状态的方案数 f[S]
对于第一种转移, ST 的转移系数为 f[TS]
考虑相邻时刻的转移, ST 的转移系数为 f[TS] ,其中 T 表示 T 置反
当然,实际情况中我们枚举 S 的补集的子集 t ,由 S 转移到 (S+t)
上面的DP已经可以算出正确答案,但状态 S 216 种,而 n 更是达到 109
考虑对于某个状态,可以通过旋转/对称进行归类
再考察初始状态能到达的状态,我们得到有效状态只有997个,但仍然无法用矩阵快速幂求解
通过某种方式可以证明(然而我并不懂),答案是关于 n 前面 M=921 项的递推式
an=k1×an1+k2×an2+...+kM×anM
我们可以用上面的方法暴力算出前 2M 项结果,通过高斯消元得出递推系数
通过 init() 函数打表保留前 M 项答案数组 a[] 及递推系数数组 k[]
后面由于是常系数齐次线性递推关系,可以用叉姐论文《线性递推关系与矩阵乘法》中的方法求解

时间复杂度: O(M2logN)

代码

/**
 * Copyright © 2016 Authors. All rights reserved.
 * 
 * FileName: D.cpp
 * Author: Beiyu Li 
 * Date: 2016-05-09
 */
#include 

using namespace std;

#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)

typedef long long LL;
typedef pair<int, int> Pii;

const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;

const int maxs = (1 << 16) + 5;
const int maxt = 2452 + 5;              // max normalized states
const int maxc = 997 + 5;               // max reachable states
const int maxm = 921 + 5;               // max coefficient of linear recurrence
const int mod = 1000000007;

int rot[] = {4, 1, 8, 2};
int rotate(int S)
{
        int ret = 0;
        for (int p = 0; p < 16; p += 4)
                rep(i,4) if ((S >> p) & (1 << i)) ret |= rot[i] << p;
        return ret;
}

int flip(int S, int f)
{
        int ret = 0, mask = (f & 4) << 1 | f;
        rep(i,16) if (S & (1 << i)) ret |= 1 << (i ^ mask);
        return ret;
}

int norm(int S)
{
        int ret = S;
        rep(pos,16) {
                int T = flip(S, pos >> 1);
                ret = min(ret, pos & 1? rotate(T) :T);
        }
        return ret;
}

void gcd(int a, int b, int &g, int &x0, int &y0)
{
        if (!b) g = a, x0 = 1, y0 = 0;
        else gcd(b, a % b, g, y0, x0), y0 -= x0 * (a / b);
}

int inv(int a)
{
        int g, x0, y0;
        gcd(a, mod, g, x0, y0);
        return (x0 + mod) % mod;
}

int gauss(int a[][maxm], int n, int ret[])
{
        int rank = 0;
        bool free[maxc];
        memset(free, true, sizeof(free));
        rep(i,n) {
                int r = rank;
                while (r < n && !a[r][i]) ++r;
                if (r == n) continue;
                if (r != rank) For(j,i,n) swap(a[r][j], a[rank][j]);
                for (int k = rank + 1; k < n; ++k) {
                        int tmp = (LL)a[k][i] * inv(a[i][i]) % mod;
                        for (int j = n; j >= i; --j) a[k][j] = (a[k][j] -
                                        (LL)tmp * a[rank][j] % mod + mod) % mod;
                }
                ++rank, free[i] = false;
        }
        for (int i = rank; i < n; ++i) if (a[i][n]) return -1;
        for (int i = n - 1, k = rank; i >= 0; --i) if (!free[i]) {
                ret[i] = a[--k][n];
                for (int j = i + 1; j < n; ++j) ret[i] = (ret[i] -
                                (LL)a[k][j] * ret[j] % mod + mod) % mod;
                ret[i] = (LL)ret[i] * inv(a[k][i]) % mod;
        }
        return rank;
}

int id[maxs];
int sta[maxt], tot;
int f[maxs];
int t[maxt][maxt];
bool vis[maxt];
int que[maxc], head, tail;
int g[2][maxc];
int m = 921;
int A[maxm][maxm], r[maxm];
int a[] = {121, 155969, 127027769, 249680610}; // 744708554, 4538468, ...
int k[] = {1731, 999029836, 191946633, 890219327}; // 540076999, 678513135, ...

void init()
{
        int ALL = (1 << 16) - 1;

        // find normalized states
        memset(id, -1, sizeof(id));
        For(T,0,ALL) if (~__builtin_popcount(T) & 1) {
                int S = norm(T);
                if (~id[S]) id[T] = id[S];
                else id[S] = tot, sta[tot++] = S;
        }

        // calc number of constructing state S from state 0
        f[0] = 1;
        rep(k,4) rep(j,2) rep(i,2) {
                int p = (k << 2) | (j << 1) | i;
                For(S,0,ALL) if (~id[S] && (~S & (1 << p))) {
                        if (k < 3 && (~S & (16 << p))) f[S|17<if (j < 1 && (~S & (4 << p))) f[S|5<if (i < 1 && (~S & (2 << p))) f[S|3<// calc transfer coefficient of normalized states
        rep(i,tot) {
                int S = sta[i] ^ ALL;
                for (int T = S;; T = (T - 1) & S) {
                        if (~id[T]) t[i][id[(sta[i]|T)^ALL]] += f[T];
                        if (!T) break;
                }
        }

        // keep states which can be reached from state 0
        vis[0] = true, que[tail++] = 0;
        while (head < tail) {
                int i = que[head++];
                rep(j,tot) if (t[i][j] && !vis[j])
                        vis[j] = true, que[tail++] = j;
        }

        // calc coefficient matrix of gauss
        g[1][0] = 1;
        rep(i,m*2) {
                int o = i & 1;
                memset(g[o], 0, sizeof(g[o]));
                rep(j,tail) rep(k,tail) g[o][j] = (g[o][j] +
                                (LL)(g[o^1][k] * t[que[k]][que[j]])) % mod;
                if (i <= m) {
                        A[0][i] = g[o][0];
                } else {
                        rep(j,m) A[i-m][j] = A[i-m-1][j+1];
                        A[i-m][m] = g[o][0];
                }
        }

        assert(~gauss(A, m, r));

        FILE *fout = fopen("D.arg", "w");
        rep(i,m) fprintf(fout, "%s%d", i? ", ": "int a[] = {", A[0][i]);
        fprintf(fout, "};\n");
        rep(i,m) fprintf(fout, "%s%d", i? ", ": "int k[] = {", r[m-i-1]);
        fprintf(fout, "};\n");
        fclose(fout);
}

void multiply(int u[], int v[])
{
        static int t[maxm*2];
        memset(t, 0, sizeof(t));
        rep(i,m) rep(j,m) t[i+j] = (t[i+j] + (LL)u[i] * v[j]) % mod;
        for (int i = 2 * m - 2; i >= m; --i) {
                rep(j,m) t[i-j-1] = (t[i-j-1] + (LL)k[j] * t[i]) % mod;
                t[i] = 0;
        }
        memcpy(u, t, sizeof(t));
}

int calc(int n)
{
        static int u[maxm*2], v[maxm*2];
        memset(u, 0, sizeof(u)), u[0] = 1;
        for (int p = 1; n; p <<= 1, n >>= 1) {
                if (p < m) memset(v, 0, sizeof(v)), v[p] = 1;
                else multiply(v, v);
                if (n & 1) multiply(u, v);
        }
        int ret = 0;
        rep(i,m) ret = (ret + (LL)u[i] * a[i]) % mod;
        return ret;
}

int main()
{
        int T, cas = 0;
        scanf("%d", &T);

        // init();
        while (T--) {
                int n;
                scanf("%d", &n);
                printf("Case #%d: %d\n", ++cas, calc(n - 1));
        }

        return 0;
}

Problem E. Expectation

题目大意

对于初始为0的 2×n 矩阵,每次随机选择一个子矩阵置1,问将整个矩阵全部置为1的期望步数

算法思路

首先想到,最终的期望等于每一步失败概率的累加和
考虑用二进制表示当前矩阵的状态,其中全为1的子矩阵个数为k,0的个数为 o
如果要保证该状态不变,则每次只能选择这k个子矩阵中的某个涂色,概率 p=k3n(n+1)/2
由容斥原理,该状态对于失败概率的贡献为 i=1pi(1)o1=11p(1)o1 ,也是对最后期望的贡献
不难发现,对结果有影响的只有 k o ,可以将状态合并起来
f[i][u][v][k][o] 表示前 i 列,第一行末尾有 u 个1,第二行末尾有 v 个1,其中有 k 个全1的子矩阵,0个数奇偶性为 o 的状态有多少个
则向 i+1 列添加0/1时有4种方案,分别转移即可
计算答案时枚举某一列存在0的状态,令后面的列全为1,重新计算 k 的值及概率 p
根据 o 的奇偶性对答案进行贡献即可

时间复杂度: O(n3×3n(n+1)2×8)

代码

/**
 * Copyright © 2016 Authors. All rights reserved.
 * 
 * FileName: E.cpp
 * Author: Beiyu Li 
 * Date: 2016-05-10
 */
#include 

using namespace std;

#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)

typedef long long LL;
typedef pair<int, int> Pii;

const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;

const int maxn = 20 + 1;
const int maxk = 3 * 20 * (20 + 1) / 2 + 1;

long double f[maxn][maxn][maxn][maxk][2];

inline int C(int n) { return 3 * n * (n + 1) / 2; }

void init()
{
        memset(f, 0, sizeof(f)), f[0][0][0][0][0] = 1;
        For(i,0,19) For(u,0,i) For(v,0,i) For(k,0,C(i)) rep(o,2) {
                f[i+1][0][0][k][o] += f[i][u][v][k][o];
                f[i+1][u+1][0][k+u+1][o^1] += f[i][u][v][k][o];
                f[i+1][0][v+1][k+v+1][o^1] += f[i][u][v][k][o];
                f[i+1][u+1][v+1][k+u+v+min(u,v)+3][o] += f[i][u][v][k][o];
        }
}

double solve(int n)
{
        long double ret = 0;
        For(i,0,n) For(u,0,i) For(v,0,i) if (!u || !v) For(k,0,C(i)) {
                int c = k + u * (n - i) + v * (n - i) + C(n - i);
                if (c == C(n)) continue;
                ret += f[i][u][v][k][1] / (1 - (long double)c / C(n));
                ret -= f[i][u][v][k][0] / (1 - (long double)c / C(n));
        }
        return ret;
}

int main()
{
        int T, cas = 0;
        scanf("%d", &T);

        init();
        while (T--) {
                int n;
                scanf("%d", &n);
                printf("Case #%d: %.6f\n", ++cas, solve(n));
        }

        return 0;
}

Problem F. Fee

题目大意

N 个人,每个人有一定的存款,他们想通过转账让所有人最后的钱相等
转账时银行按比例收取手续费,问最后每个人最多能有多少钱

算法思路

二分答案,考察超过 mid 的钱在交付手续费后能否填补不够 mid 的钱

时间复杂度: O(NlogV)

代码

/**
 * Copyright © 2016 Authors. All rights reserved.
 * 
 * FileName: F.cpp
 * Author: Beiyu Li 
 * Date: 2016-05-10
 */
#include 

using namespace std;

#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)

typedef long long LL;
typedef pair<int, int> Pii;

const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;

const double eps = 1e-10;
int sgn(double x) { return x < -eps? -1: x > eps; }

const int maxn = 100000 + 5;

int n;
double k, a[maxn];

bool check(double b)
{
        double ret = 0;
        rep(i,n) {
                if (a[i] < b) ret -= (b - a[i]);
                if (a[i] > b) ret += (a[i] - b) * (1 - k);
        }
        return sgn(ret) >= 0;
}

int main()
{
        int T, cas = 0;
        scanf("%d", &T);

        while (T--) {
                scanf("%d%lf", &n, &k);
                double l = 0, r = 0;
                rep(i,n) scanf("%lf", &a[i]), r += a[i];
                r /= n;
                while (sgn(l - r) < 0) {
                        double mid = (l + r) / 2;
                        if (check(mid)) l = mid;
                        else r = mid;
                }
                printf("Case #%d: %.6f\n", ++cas, l);
        }

        return 0;
}

Problem G. Game

题目大意

Alice和Bob玩游戏,Bob有一个可以按照设定的概率随机输出 1 N 的生成器,而Alice知道设定的概率
如果Alice猜对生成的数,则Alice赢1元钱
如果Alice猜的是生成的数减1,则Bob赢1元钱
问Bob在设置最优的概率,同时Alice选择最优策略的条件下,Alice赢钱的期望为多少

算法思路

若生成器输出 i 的概率为 Pi ,则Alice猜 i 时赢钱的期望为 PiPi+1 ,其中 PN+1 为0
显然Alice会选择概率下降最大的数,而Bob则要最小化这一值
不难发现,当所有概率下降的值相同,即 Pi 数列构成等差数列时,这一值最小
通过方程解得,答案为 2N(N+1)

时间复杂度: O(1)

代码

/**
 * Copyright © 2016 Authors. All rights reserved.
 * 
 * FileName: G.cpp
 * Author: Beiyu Li 
 * Date: 2016-05-10
 */
#include 

using namespace std;

#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)

typedef long long LL;
typedef pair<int, int> Pii;

const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;

int main()
{
        int T, cas = 0;
        scanf("%d", &T);

        while (T--) {
                int n;
                scanf("%d", &n);
                printf("Case #%d: %.6f\n", ++cas, 2.0 / n / (n + 1));
        }

        return 0;
}

Problem H. Heresy

题目大意

输入 n m ,求

i=1nj=1mi2j2gcd(i,j)

取模1000000007的值

算法思路

换个方向思考,先枚举 gcd(i,j) 的值 g ,则上式等于

gi=1ngj=1mgi2j2g5(gcd(i,j)==1)

其中的 gcd(i,j)==1 可使用莫比乌斯函数做容斥,即等于
gdi=1ng×dj=1mg×di2j2g5d4μ(d)

D=g×d 可得,上式等于
Dd|Di=1nDj=1mDi2j2D5dμ(d)

提取得
DD5d|Dμ(d)di=1nDi2j=1mDj2

至此,我们可以先对每个 D 进行预处理,得到
fD=D5d|Dμ(d)d

对于每组询问,由于 ND MD 的值只有根号级别的个数
可以通过上述值的变化枚举 D ,将 fD 区间和与 i j 平方和的乘积加到答案中即可

时间复杂度: O(NlogN+T×(N+M))

代码

/**
 * Copyright © 2016 Authors. All rights reserved.
 * 
 * FileName: H.cpp
 * Author: Beiyu Li 
 * Date: 2016-05-10
 */
#include 

using namespace std;

#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)

typedef long long LL;
typedef pair<int, int> Pii;

const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;

const int maxn = 1000000 + 5;
const int mod = 1000000007;

int pow_mod(int a, int b)
{
        int ret = 1;
        for (; b; a = (LL)a * a % mod, b >>= 1)
                if (b & 1) ret = (LL)ret * a % mod;
        return ret;
}

bool isp[maxn];
int prime[maxn], sz;
int mu[maxn];
int f[maxn], pre[maxn], sum[maxn];

void init()
{
        memset(isp, true, sizeof(isp));
        isp[0] = isp[1] = false, mu[1] = 1;
        for (int i = 2; i < maxn; ++i) {
                if (isp[i]) prime[sz++] = i, mu[i] = -1;
                for (int j = 0; j < sz && prime[j] * i < maxn; ++j) {
                        isp[prime[j]*i] = false;
                        if (i % prime[j] == 0) {
                                mu[prime[j]*i] = 0;
                                break;
                        }
                        mu[prime[j]*i] = mu[prime[j]] * mu[i];
                }
        }
        for (int d = 1; d < maxn; ++d) {
                int k = (mu[d] * pow_mod(d, mod - 2) + mod) % mod;
                for (int i = 1; d * i < maxn; ++i) f[d*i] = (f[d*i] + k) % mod;
        }
        rep(i,maxn) if (i) {
                f[i] = (LL)f[i] * pow_mod(i, 5) % mod;
                pre[i] = (pre[i-1] + f[i]) % mod;
                sum[i] = (sum[i-1] + (LL)i * i) % mod;
        }
}

int main()
{
        int T, cas = 0;
        scanf("%d", &T);

        init();
        while (T--) {
                int n, m, ret = 0;
                scanf("%d%d", &n, &m);
                for (int d = 1, nd; d <= n && d <= m; d = nd + 1) {
                        nd = min(n / (n / d), m / (m / d));
                        ret = (ret + (LL)(pre[nd] - pre[d-1] + mod) *
                                        sum[n/d] % mod * sum[m/d]) % mod;
                }
                printf("Case #%d: %d\n", ++cas, ret);
        }

        return 0;
}

Problem I. International Morse Code

题目大意

输入 N 个单词,输出对应摩尔斯电码的按键状态,其中
“点”按1单位时间
“线”按3单位时间
元素间空1个单位
字母间空3个单位
单词间空7个单位
按下输出’=’,空白输出’.’

GDCPC2016题解 by lby@SYSU | Asiimov_第1张图片

算法思路

按照上面说的模拟…

时间复杂度: O(N)

代码

/**
 * Copyright © 2016 Authors. All rights reserved.
 * 
 * FileName: I.cpp
 * Author: Beiyu Li 
 * Date: 2016-05-10
 */
#include 

using namespace std;

#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)

typedef long long LL;
typedef pair<int, int> Pii;

const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;

string code[] = {
        ".-", "-...", "-.-.", "-..", ".", "..-.", "--.",
        "....", "..", ".---", ".-.-", ".-..", "--", "-.",
        "---", ".--.", "--.-", ".-.", "...", "-",
        "..-", "...-", ".--", "-..-", "-.--", "--..",
        ".----", "..---", "...--", "....-", ".....",
        "-....", "--...", "---..", "----.", "-----"
};

void print(char ch)
{
        int v = (isalpha(ch)? tolower(ch) - 'a': 26 + (ch - '0' + 9) % 10);
        rep(i,code[v].length()) {
                if (i) putchar('.');
                printf("%s", code[v][i] == '.'? "=": "===");
        }
}

int main()
{
        int T, cas = 0;
        scanf("%d", &T);

        while (T--) {
                int n;
                scanf("%d", &n);
                printf("Case #%d: ", ++cas);
                rep(i,n) {
                        if (i) rep(j,7) putchar('.');
                        char buf[32];
                        scanf("%s", buf);
                        for (int j = 0; buf[j]; ++j) {
                                if (j) rep(k,3) putchar('.');
                                print(buf[j]);
                        }
                }
                puts("");
        }

        return 0;
}

Problem J. Just another binary tree

题目大意

问大小为 N 的堆中序遍历后排第 k 的节点编号

算法思路

DFS
对于某棵子树计算其左子树的大小
与当前的k比较以选择进入的分支
进入右子树时注意更新k的值

时间复杂度: O(log2N)

代码

/**
 * Copyright © 2016 Authors. All rights reserved.
 * 
 * FileName: J.cpp
 * Author: Beiyu Li 
 * Date: 2016-05-10
 */
#include 

using namespace std;

#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)

typedef long long LL;
typedef pair<int, int> Pii;

const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;

int dfs(int v, int n, int k)
{
        int p = 1;
        while (p * 2 <= n) p *= 2;
        int l = p / 2 - 1 + min(n - (p - 1), p / 2);
        if (n == 1 || k == l + 1) return v;
        return k <= l? dfs(v * 2, l, k): dfs(v * 2 + 1, n - l - 1, k - l - 1);
}

int main()
{
        int T, cas = 0;
        scanf("%d", &T);

        while (T--) {
                int n, k;
                scanf("%d%d", &n, &k);
                printf("Case #%d: %d\n", ++cas, dfs(1, n, k));
        }

        return 0;
}

Problem K. Kay’s function

题目大意

f[0]=a
f[1]=b
f[n]=f[n1] xor f[n2]
输入 a b n ,输出 f[n]

算法思路

显然这个序列由 a,b,a xor b,... 不断循环构成,故输出 f[n mod 3] 即可

时间复杂度: O(1)

代码

/**
 * Copyright © 2016 Authors. All rights reserved.
 * 
 * FileName: K.cpp
 * Author: Beiyu Li 
 * Date: 2016-05-10
 */
#include 

using namespace std;

#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)

typedef long long LL;
typedef pair<int, int> Pii;

const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;

int main()
{
        int T, cas = 0;
        scanf("%d", &T);

        while (T--) {
                int n, f[3];
                scanf("%d%d%d", &f[0], &f[1], &n);
                f[2] = f[0] ^ f[1];
                printf("Case #%d: %d\n", ++cas, f[n%3]);
        }

        return 0;
}

致谢

感谢mayf3同学提供的数据及部分指导

你可能感兴趣的:(GDCPC)