GDCPC2016题解 by [email protected] | Asiimov

Problem A. ABCD

题目大意

给出一个四边形四条边AB、CD、AD、BC及两条对角线AC、BD的长度,问这个四边形的顶点能否在一个圆上

算法思路

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

时间复杂度: O(1)

代码

/**
 * Copyright ? 2016 Authors. All rights reserved.
 *
 * FileName: A.cpp
 * Author: Beiyu Li <[email protected]>
 * Date: 2016-05-09
 */
#include <bits/stdc++.h>

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 <[email protected]>
 * Date: 2016-05-09
 */
#include <bits/stdc++.h>

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 <[email protected]>
 * Date: 2016-05-09
 */
#include <bits/stdc++.h>

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<Pii> 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]

对于第一种转移,S→T的转移系数为f[T?S]

考虑相邻时刻的转移,S→T的转移系数为f[T′?S],其中T′表示T置反

当然,实际情况中我们枚举S的补集的子集t,由S转移到(S+t)′

上面的DP已经可以算出正确答案,但状态S有216种,而n更是达到109

考虑对于某个状态,可以通过旋转/对称进行归类

再考察初始状态能到达的状态,我们得到有效状态只有997个,但仍然无法用矩阵快速幂求解

通过某种方式可以证明(然而我并不懂),答案是关于n前面M=921项的递推式

即an=k1×an?1+k2×an?2+...+kM×an?M

我们可以用上面的方法暴力算出前2M项结果,通过高斯消元得出递推系数

通过init()函数打表保留前M项答案数组a[]及递推系数数组k[]

后面由于是常系数齐次线性递推关系,可以用叉姐论文《线性递推关系与矩阵乘法》中的方法求解

时间复杂度: O(M2logN)

代码

/**
 * Copyright ? 2016 Authors. All rights reserved.
 *
 * FileName: D.cpp
 * Author: Beiyu Li <[email protected]>
 * Date: 2016-05-09
 */
#include <bits/stdc++.h>

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<<p] += f[S];
                        if (j < 1 && (~S & (4 << p))) f[S|5<<p] += f[S];
                        if (i < 1 && (~S & (2 << p))) f[S|3<<p] += f[S];
                }
        }

        // 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=1∞pi(?1)o?1=11?p(?1)o?1,也是对最后期望的贡献

不难发现,对结果有影响的只有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 <[email protected]>
 * Date: 2016-05-10
 */
#include <bits/stdc++.h>

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 <[email protected]>
 * Date: 2016-05-10
 */
#include <bits/stdc++.h>

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时赢钱的期望为Pi?Pi+1,其中PN+1为0

显然Alice会选择概率下降最大的数,而Bob则要最小化这一值

不难发现,当所有概率下降的值相同,即Pi数列构成等差数列时,这一值最小

通过方程解得,答案为2N(N+1)

时间复杂度: O(1)

代码

/**
 * Copyright ? 2016 Authors. All rights reserved.
 *
 * FileName: G.cpp
 * Author: Beiyu Li <[email protected]>
 * Date: 2016-05-10
 */
#include <bits/stdc++.h>

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=1n∑j=1mi2j2gcd(i,j)

取模1000000007的值

算法思路

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

∑g∑i=1ng∑j=1mgi2j2g5(gcd(i,j)==1)

其中的gcd(i,j)==1可使用莫比乌斯函数做容斥,即等于

∑g∑d∑i=1ng×d∑j=1mg×di2j2g5d4μ(d)

令D=g×d可得,上式等于

∑D∑d|D∑i=1nD∑j=1mDi2j2D5dμ(d)

提取得

∑DD5∑d|Dμ(d)d∑i=1nDi2∑j=1mDj2

至此,我们可以先对每个D进行预处理,得到

fD=D5∑d|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 <[email protected]>
 * Date: 2016-05-10
 */
#include <bits/stdc++.h>

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个单位

按下输出’=’,空白输出’.’

算法思路

按照上面说的模拟…

时间复杂度: O(N)

代码

/**
 * Copyright ? 2016 Authors. All rights reserved.
 *
 * FileName: I.cpp
 * Author: Beiyu Li <[email protected]>
 * Date: 2016-05-10
 */
#include <bits/stdc++.h>

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 <[email protected]>
 * Date: 2016-05-10
 */
#include <bits/stdc++.h>

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[n?1] xor f[n?2]

输入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 <[email protected]>
 * Date: 2016-05-10
 */
#include <bits/stdc++.h>

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同学提供的数据及部分指导

时间: 2024-08-22 15:27:35

GDCPC2016题解 by [email protected] | Asiimov的相关文章

@bzoj - [email&#160;protected] [POI2015] Logistyka

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 维护一个长度为 n 的序列,一开始都是 0,支持以下两种操作: 1.U k a 将序列中第 k 个数修改为 a. 2.Z c s 在这个序列上,每次选出 c 个正数,并将它们都减去 1,询问能否进行 s 次操作. 每次询问独立,即每次询问不会对序列进行修改. input 第一行包含两个

@bzoj - [email&#160;protected] [POI2015] Odwiedziny

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵 n 个点的树,树上每条边的长度都为 1 ,第 i 个点的权值为 a[i]. Byteasar 会按照某个 1 到 n 的全排列 b 走 n-1 次,第 i 次他会从 b[i] 点走到 b[i+1] 点,并且这一次的步伐大小为 c[i]. 对于一次行走,假设起点为 x,终点为

@bzoj - [email&#160;protected] [POI2015] ?asuchy

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 圆桌上摆放着 n 份食物,围成一圈,第 i 份食物所含热量为 c[i]. 相邻两份食物之间坐着一个人,共有 n 个人.每个人有两种选择,吃自己左边或者右边的食物.如果两个人选择了同一份食物,这两个人会平分这份食物,每人获得一半的热量. 假如某个人改变自己的选择后(其他 n-1 个人的选

@noi.ac - [email&#160;protected] cleaner

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 小Q计划在自己的新家中购置一台圆形的扫地机器人.小Q的家中有一个宽度为 m 的走廊,走廊很长,如果将这个走廊的俯视图画在平面直角坐标系上的话,那么走廊的两堵墙可以分别看作直线 y=0 和直线 y=m,两堵墙之间的部分代表走廊. 小Q会按照顺序依次在走廊中安置 n 个家具.第 i 个家具

@noi.ac - [email&#160;protected] game

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 小 Q 和小 T 正在玩一种双人游戏.m 张木牌从左往右排成一排,第 i 张木牌上写着一个正整数 bi.小 Q 和小 T 轮流行动总计 m 轮,小 Q 先手.在每一轮中,行动方需要选择最左或者最右的一张木牌并将其拿走.游戏最后每个人的得分即为他拿走的木牌上写着的数字之和,得分较大的一方

@noi.ac - [email&#160;protected] 老头子的话

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 老头子是小学校长,小学生(大哥)们都很听老头子的话.一天,老头子给小学生(大哥)们发苹果吃. 一共有 n 个小学生(大哥),老头子每一次会等概率选择一位小学生(大哥)并给他一个苹果.一个小学生(大哥)变得开心当且仅当他拥有的苹果数 ≥k. 因为老头子年纪大了,所以他想要你告诉他,期望多

@bzoj - [email&#160;protected] 礼物

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 一年一度的圣诞节快要来到了.每年的圣诞节小E都会收到许多礼物,当然他也会送出许多礼物.不同的人物在小E心目中的重要性不同,在小E心中分量越重的人,收到的礼物会越多. 小E从商店中购买了n件礼物,打算送给m个人,其中送给第i个人礼物数量为wi. 请你帮忙计算出送礼物的方案数(两个方案被认

@codeforces - [email&#160;protected] Rotate Columns (hard version)

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个 n*m 的矩阵 A. 定义一次操作为将矩阵的某一列竖着循环移位,你可以对任意列做任意次操作. 定义 ri 为第 i 行的最大值,最大化 r1 + r2 + ... + rn. Input 第一行一个整数 t (1≤t≤40),表示数据组数. 每组数据第一行包含两个整数 n m

@codeforces - [email&#160;protected] Employment

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 有 m 个城市围成一个圆环,编号为 1~m. 某公司有 n 个职员住在 m 个城市,依次住在编号为 a1, a2, ..., an 的城市(可能住在同一城市).且该公司有 n 个工作场所,依次在 b1, b2, ..., bn(一样可以在同一城市). 现在要将职员与城市一一匹配,使得每