T1

期望得分:****(没想得几分,有分就行).瞎凑了一个公式..

题目描述

最近公共祖先(Lowest Common Ancestor,LCA)是指在一个树中同时拥有给定的两个点作为后代的最深的节点。
为了学习最近公共祖先,你得到了一个层数为 \(n + 1\) 的满二叉树,其中根节点的深度为 \(0\),其他
节点的深度为父节点的深度 \(+1\) 。你需要求出二叉树上所有点对 \((i,j)\),(\(i,j\) 可以相等,也可以 \(i > j\)
的最近公共祖先的深度之和对 \(1e9 + 7\) 取模后的结果。

输入格式

一行一个整数 n 。

输出格式

一行一个整数表示所有点对 (i,j),(i,j 可以相等,也可以 i > j)的最近公共祖先的深度之和对
10 9 + 7 取模后的结果。
____________________________

样例 input output
1 2 22
2 19260817 108973412

数据范围

对于\(20\%\)的数据, \(n\leq 10\).
对于\(50\%\)的数据, \(n\leq10^6\).
对于\(100\%\)的数据,\(n\leq10^9\).

解题思路:

在节点i处的子树的节点数是n-i+1,然后因为这两个子树各取一个点的LCA才是当前的i节点.

以LCA为根节点,然后再两个子树中选,那么点的个数就是\(\frac{n-i+1}{2}\ast \frac{n+i+1}{2} \ast 2\).

对于每一个子树来说,子树中的每一个点与LCA本身的LCA也是LCA(有点绕),那么他的贡献就变成了\((2^{n-i+1}-1) \ast i \ast 2\)

因为自己和自己的LCA也要贡献答案,设上边两个贡献的和为\(tmp\),那么贡献就是这样的\((tmp + i) \ast 2^i\)

那么\(ans\)就是:\(\displaystyle \sum^{n}_{i=1}{((\frac{n-i+1}{2}\ast \frac{n+i+1}{2} \ast 2 +(2^{n-i+1}-1) \ast i \ast 2 ) + i) \ast 2 ^ i}\)

把上边这一坨都化简之后就是\(\sum_{i = 1}^{n}{( 2^{2n - i + 1} - 2^i)\ast i}\)

然后我们设
\(p = 2 ^ {2n} - 2^1 + 2 * (2 ^{2n - 1} - 2 ^ 2) + 3 * (2 ^ {2n - 2}- 2 ^ 3) + ...+ n * (2 ^ {n + 1} - 2 ^ n)\)

\(2p = 2 ^ {2n +1} - 2 ^ 2 +2 * (2^{2n} - 2^3) + 3 * (2^{2n - 1} - 2^4) + ... +n*(2^{n + 2} - 2^ {n+ 1})\)

展开之后:
\(p = 2 ^ {2n} - 2^1 + 2 * 2 ^{2n - 1} - 2 * 2 ^ 2 + 3 * 2 ^ {2n - 2}-3* 2 ^ 3 + ...+ n * 2 ^ {n + 1} - n * 2 ^ n\)

\(2p = 2 ^ {2n +1} - 2 ^ 2 +2 * 2^{2n} - 2*2^3 + 3 * 2^{2n - 1} - 3*2^4 + ... +n*2^{n + 2} - n*2^ {n+ 1}\)

\(2p - p = \sum_{i = 1}^ {n}{2^I} + \sum_{i = n +1}^ {2n} 2^i - n * 2 ^ {n + 1}\)

化简之后答案为:

\(ans = 2 ^{2n + 2} + 2^ {n + 1} -(n + 1) * 2^{n + 2} - 2\)

然后就可以用快速幂\(\O(1)\)求了.

code

#include <bits/stdc++.h>
#define N 100010
#define M 1010
#define ll long long

using namespace std;
ll n, sum;
const int mod = 1e9 + 7;

ll read() {
    int s = 0, f = 0; char ch = getchar();
    while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
    while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
    return f ? -s : s;
}

ll q_pow(ll a, ll b) {
    ll ans = 1;
    while (b) {
        if (b & 1) ans = (ans * a) % mod;
        a = (a * a) % mod;
        b >>= 1;
    }
    return ans;
}

signed main() {
//  freopen("commonants.in", "r", stdin);
//  freopen("commonants.out", "w", stdout);
    n = read();
    ll q = q_pow(2, 2 * n + 2), w = q_pow(2, n);
    w = (w * (4 * n + 2) % mod) % mod;
    sum = q - w - 2;
    cout << (sum + mod) % mod;
}

T2 即时战略 (rts.c/cpp/pas)

期望得分:40

实际得分:40(暴力求解)

题目描述

\(LKP\)在玩一个即时战略 (Real Time Strategy) 游戏。与大多数同类游戏类似,这个游戏的地图是平面的,并且玩家都有一个基地。
\(LKP\) 的对手杰哥的基地是一个 \(w \ast h\) 的矩形。其中矩形的每个格子都有一个建筑,每个建筑都有一个重要度。其中第 \(i\) 行第 \(j\) 列的格子中的建筑的重要度为 \(w_{ij}\)
\(LKP\)决定轰炸杰哥的基地。他可以选择杰哥基地的任何一个格子释放一个能量为 \(p\) 的炸弹。释放以后,这个格子的建筑会受到 \(p\) 的摧毁度。炸弹产生的冲击波可以向四个方向扩散,每扩散一格能量值会减少 \(1\) 。即释放位置相邻的 \(4\) 个格子会受到 \(p − 1\) 的摧毁度,再向外的 \(8\) 个格子会受到 \(p − 2\)的摧毁度 ... 直到能量值减为 \(0\) ,形式化的讲,如果在第 \(x\) 行第 \(y\) 列释放炸弹,那么第 \(i\) 行第 \(j\) 列的格子受到的摧毁度 \(d_{ij} = max(0,p − (| x − i | + | y − j |))\)
对于每个的格子,杰哥受到的损失即为这个格子的重要度与受到的摧毁度的乘积,即 \(w_{ij} \ast d_[ij]\)
HLY 想知道,对于每一种投放炸弹的方案,杰哥受到的最小总损失和最大总损失各为多少,形式化的讲,即为
\(\sum_{i = 1}^{w} \sum_{j = 1}^{h} {w_{ij} \ast d_{ij}}\)
的最小值与最大值。

输入格式

\(1\) 行三个整数 \(w,h,p\)
接下来 $w $行,每行 \(h\) 个整数。从第 \(2\) 行开始第 \(i\) 行第 \(j\) 个整数表示 \(w_{ij}\)

输出格式

一行两个数,表示杰哥受到的最小总损失和最大总损失,用空格隔开。

数据范围

对于 \(100\%\) 的数据,\(1 ≤ n,m ≤ 400\),\(1 ≤ p ≤ 200\),\(0 ≤ w_{ij} ≤ 10 ^5\)
子任务 1 (\(10\) 分) :满足 \(p = 1\)
子任务 2 (\(30\) 分) :满足 \(1 ≤ n,m ≤ 40\)
子任务 3 (\(60\) 分) :没有特殊限制。

解题思路:

把这个东西翻转45°看成一个斜着的矩形,然后中间的那一部分类似菱形的东西就变成了矩形
然后就能用前缀和做了,做的时候一层一层的加上.从最外层开始,
最外边加一,往里一次加2.3.4然后枚举每个点做上述操作,然后就做完了.

code

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
const int maxn = 1010;

int g[maxn][maxn], trans[maxn][maxn];
long long s[maxn][maxn];

int main() {
//  freopen("rts.in", "r", stdin);
//  freopen("rts.out", "w", stdout);
    int n, m, p;
    long long mx = 0x8000000000000000ll, mn = 0x7fffffffffffffffll;
    scanf("%d%d%d", &n, &m, &p);
    for (int i = 1; i <= n; ++i) 
        for (int j = 1; j <= m; ++j) 
            scanf("%d", g[m + i - j] + i + j - 1);
    for (int i = 1; i < n + m; ++i) 
        for (int j = 1; j < n + m; ++j) 
            s[i][j] = g[i][j] + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
    for (int i = 1; i <= n; ++i) 
        for (int j = 1; j <= m; ++j) {
            int x = m + i - j, y = i + j - 1;
            long long ans = 0;
            for (int k = 0; k < p; ++k) {
                int x1 = std::max(x - k, 1), y1 = std::max(y - k, 1);
                int x2 = std::min(x + k, n + m - 1), y2 = std::min(y + k, n + m - 1);
                ans += s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1];
            }
            mx = std::max(mx, ans), mn = std::min(mn, ans);
        }
    std::cout << mn << " " << mx << std::endl;
    return 0;
}