Rank 445/3193. AC 6/13.
A. 清楚姐姐学信息论
题意
给定 
思路
显然,我们只需比较 
时间复杂度:
对应AC代码
import java.util.*;
public class Main{
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int a = scanner.nextInt(), b = scanner.nextInt();
        if ((a == 2 && b == 3) || (a == 3 && b == 2)) System.out.println(3);
        else System.out.println(Math.min(a, b));
    }
} 淦,还有特判,被坑了
B. 清楚姐姐学构造
题意
给定数组 
$\left{\begin{aligned}
ai \equiv a{N-i-1}\ (mod\ m) \
bi \equiv -b{N-i-1}\ (mod\ m) \
c_i \equiv a_i+b_i\ (mod\ m)
\end{aligned}
\right.$
若可构造,输出 
一句话题意
在模系下构造两个数列,一个满足奇函数性质,另一个满足偶函数性质,两个数列的和为任意给定数列。
思路
考虑到对称性,我们不妨设 $ai=a{N-i-1}=x,bi=m-b{N-i-1}=y$。
那么,$ai+b_i=x+y,a{N-i-1}+b_{N-i-1}=x+m-y$。
代入第三个式子,我们可以得到 $x+y \equiv ci\ (mod\ m),x-y \equiv c{N-i-1}\ (mod\ m)$。
两式相加,$2x \equiv ci+c{N-i-1}\ (mod\ m)
因而,我们只需判断分子的奇偶性,然后即可计算出 
同理可得 
若数组的长度为奇数,那么将会多出来一项 
遍历输出即可。
时间复杂度:
对应AC代码
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
#define int long long
int a[N], b[N], c[N];
signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int n, m;
    cin >> n >> m;
    for(int i=0;i<n;i++) cin >> c[i];
    for(int i=0;i<n/2;i++){
        int k1 = c[i] + c[n - i - 1], k2 = c[i] - c[n - i - 1], x, y;
        if(k1 % 2 == 0){
            if(m == 2) {
                x = m + k1;
                y = m + k2;
            }
            else {
                x = m * 2L + k1;
                y = m * 2L + k2;
            }
        }else{
            if(m == 2){
                cout << "NO\n";
                return 0;
            }else {
                x = m + k1;
                y = m + k2;
            }
        }
        x /= 2;
        y /= 2;
        a[i] = a[n - i - 1] = x;
        b[i] = y;
        b[n - i - 1] = m - y;
    }
    if(n % 2 == 1) {
        a[n / 2] = c[n / 2];
        b[n / 2] = 0;
    }
    cout << "YES\n";
    for(int i=0;i<n;i++) cout << a[i] << ' ';
    cout << '\n';
    for(int i=0;i<n;i++) cout << b[i] << ' ';
} 不会有人暴力吧((
C. 清楚姐姐学01背包(Easy Version)
题意
给定 
现在,枚举每个物品,将该物品去除后,得到最大价值 $Val’i
思路
鉴于本题数据量很小,我们可以直接按照题面进行暴力模拟,对每个物品去掉后的情况进行 
时间复杂度:
对应AC代码
#include <bits/stdc++.h>
using namespace std;
const int N = 110, inf = 0x3f3f3f3f;
#define int long long
int n, m;
int w[N], v[N], dp[N];
signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    for(int i=1;i<=n;i++){
        cin >> w[i] >> v[i];
    }
    for (int i = 1; i <= n; i++)
        for (int l = m; l >= w[i]; l--) {
            dp[l] = max(dp[l], dp[l - w[i]] + v[i]);
        }
    int maxx = dp[m];
    for(int i=1;i<=n;i++){
        memset(dp, 0, sizeof dp);
        for (int p = 1; p <= n; p++) {
            int cw = p == i ? 0 : w[p], cv = p == i ? 0 : v[p];
            for (int l = m; l >= cw; l--) {
                dp[l] = max(dp[l], dp[l - cw] + cv);
            }
        }
        if(maxx > dp[m]) cout << 0 << '\n';
        else{
            memset(dp, 0, sizeof dp);
            for (int p = 1; p <= n; p++) {
                int cw = p == i ? 0 : w[p], cv = p == i ? 0 : v[p];
                for (int l = m - w[i]; l >= cw; l--) {
                    dp[l] = max(dp[l], dp[l - cw] + cv);
                }
            }
            cout << maxx - dp[m - w[i]] - v[i] + 1 << '\n';
        }
    }
} 令人感慨
D. 清楚姐姐学01背包(Hard Version)
题意
同 
思路
暴力解决不了问题了。
我们回到 
那么,我们自然可以发现,只要将 
那么 
更具体地
- 从前往后跑一遍 
背包,得到二维数组 ;再从后往前跑一遍 背包,得到二维数组 ;  - 枚举每一个物品:维护对于 
的 前一位的状态下容量的前缀最大值数组 ,对于 的 后一位的状态下容量的后缀最大值数组 。然后,遍历前 个的最大容量 ,剩余的分给去掉前 个物品后剩下物品的最大容量,那么, 的最大值即为 去掉 后的最大价值 。  - 综合步骤 
和暴力做法的最后一个 循环,我们只需将 前 个物品,第 个物品,剩下的物品 抽象为三个物品,然后利用 背包的第二层循环计算出必须将 选中的最大值 。  - 输出 
即可。  
时间复杂度:
对应AC代码
#include <bits/stdc++.h>
using namespace std;
const int N = 5010;
#define int long long
int n, m;
int w[N], v[N], dpz[N][N], dpf[N][N], mxz[N], mxf[N];
signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> w[i] >> v[i];
    }
    for (int i = 1; i <= n; i++)
        for (int j = 0; j <= m; j++) {
            dpz[i][j] = dpz[i - 1][j];
            dpf[i][j] = dpf[i - 1][j];
            if (j >= w[i]) dpz[i][j] = max(dpz[i][j], dpz[i - 1][j - w[i]] + v[i]);
            if (j >= w[n - i + 1]) dpf[i][j] = max(dpf[i][j], dpf[i - 1][j - w[n - i + 1]] + v[n - i + 1]);
        }
    for (int i = 1; i <= n; i++) {
        memset(mxz, 0, sizeof mxz);
        memset(mxf, 0, sizeof mxf);
        for (int j = 1; j <= m; j++) {
            mxz[j] = max(mxz[j - 1], dpz[i - 1][j]);
            mxf[j] = max(mxf[j - 1], dpf[n - i][j]);
        }
        int vi = 0, vp = 0;
        for (int j = 0; j <= m; j++) {
            vi = max(vi, mxz[j] + mxf[m - j]);
            if (m - j >= w[i])
                vp = max(vp, max(mxz[j] + mxf[m - j - w[i]] + v[i],
                                 mxf[j] + mxz[m - j - w[i]] + v[i]));
        }
        cout << max(0ll, vi - vp + 1) << '\n';
    }
} 居然有点想出来了,毕竟背包就是贪心嘛
E. 清楚姐姐打怪升级
题意
给定 
对于每个怪物,每个时刻初,恢复 
在 
输出在哪个时刻末可杀死所有怪物。若永远无法杀死则输出 
思路
贪心。
我们采取将一只怪物杀死再去杀另一只的做法,使每只需要杀死的怪物能恢复的生命值最小。
显然,在下次攻击前,怪物能恢复 
否则:
- 如果一击秒杀,时刻 
;  - 否则,在每次砍怪物之后,有效扣血为 
,将其与第一次剩余的血量 进行除法运算即可。注意需要特判可能出现的一次砍完的情况。  
因最后一只怪物的计算会多出一个单位等待时间,所以我们将其减去。
时间复杂度:
对应AC代码
import java.math.*;
import java.util.*;
public class Main{
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        long n = scanner.nextLong(), t = scanner.nextLong(), a = scanner.nextLong();
        long tick = 0;
        for(int i=0;i<n;i++){
            long h = scanner.nextLong(), v = scanner.nextLong();
            if(h <= a) {
                tick ++;
                continue;
            }
            if(v * t >= a){
                System.out.println(-1);
                return;
            }else {
                if ((h - a) % (a - v * t) == 0) tick += ((h - a) / (a - v * t) + 1);
                else tick += ((h - a) / (a - v * t) + 2);
            }
        }
        System.out.println((tick - 1) * t + 1);
    }
} 模拟+贪心呐
F. 清楚姐姐学树状数组
题意
构建一个 
给定 
思路
我们从 
若向左,
接着,我们先来看前序遍历的规律:对于下一个节点,若向左子树移动,那么前序遍历的值会 
而对于后序遍历,类似于前序:对于下一个节点,若向右子树移动,那么后序遍历的值会 
特别地,在后续遍历中,从 
注: 当然,我们也可以用递推的方式,初始化数组后按照找对称点的方式解题。
时间复杂度:
#include <bits/stdc++.h>
using namespace std;
#define int long long
int ans, maxx, x;
int lb(int x){
    return x & (-x);
}
void dfs(int now, bool inc) {
    if (now == x) return;
    if (now > x) {
        if (inc) ans++;
        else ans -= (now == maxx ? 1 : lb(now));
        dfs(now - lb(now) / 2ll, inc);
    } else {
        if (inc) ans += lb(now);
        else ans--;
        dfs(now + lb(now) / 2ll, inc);
    }
}
signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int k, q;
    cin >> k >> q;
    maxx = (1ll << k);
    while(q --){
        cin >> x;
        ans = 1;
        dfs(maxx, true);
        cout << ans << ' ' << x << ' ';
        ans = maxx;
        dfs(maxx, false);
        cout << ans << '\n';
    }
} 你说你这个蠢人怎么连找规律都找不出来呢.jpg
G. 清楚姐姐逛街(Easy Version)
题意
给定一个迷宫,终点按照固定方式移动,以题给字符确定方向。给定多个查询,包括一个起点,输出从起点开始到可变终点的最短路。
思路
考虑到查询数量很少,我们采用暴力的做法:
- 从起点开始 
,确定能到达的每个点的最短路径长度。  - 模拟终点移动,若遍历到的点存在路径长度小于等于当前终点移动的长度,那么输出答案。
 
时间复杂度:
对应AC代码
#include <bits/stdc++.h>
using namespace std;
const int N = 810;
#define int long long
char mp[N][N];
int dis[N][N], dx[4] = {0, 0, -1, 1}, dy[4] = {-1, 1, 0, 0};
signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int n, m, xs, ys, Q;
    cin >> n >> m >> xs >> ys >> Q;
    for(int i=0;i<n;i++) cin >> mp[i];
    memset(dis, 0x3f, sizeof dis);
    queue<pair<int, int>> q;
    q.emplace(xs, ys);
    dis[xs][ys] = 0;
    while(!q.empty()){
        auto t = q.front();
        q.pop();
        int px = t.first, py = t.second;
        for(int i=0;i<4;i++){
            int x = px + dx[i], y = py + dy[i];
            if(x >= 0 && x < n && y >= 0 && y < m && mp[x][y] != '#' && dis[x][y] > dis[px][py] + 1) {
                dis[x][y] = dis[px][py] + 1;
                q.emplace(x, y);
            }
        }
    }
    while(Q --){
        int x, y, ans, step = 1;
        cin >> x >> y;
        while(true){
            char cur = mp[x][y];
            if(cur == 'L' && mp[x][y - 1] != '#') y --;
            else if(cur == 'R' && mp[x][y + 1] != '#') y ++;
            else if(cur == 'U' && mp[x - 1][y] != '#') x --;
            else if(cur == 'D' && mp[x + 1][y] != '#') x ++;
            else {
                if(dis[x][y] == -1) {
                    ans = -1;
                    break;
                }
            }
            if(dis[x][y] <= step) {
                ans = step;
                break;
            }
            step ++;
        }
        cout << ans << '\n';
    }
} 为啥BFS要这么写才对捏
H. 清楚姐姐逛街(Hard Version)
待补充,倍增+二分答案
I. 清楚姐姐采蘑菇
待补充,莫队+单调性
J. 清楚姐姐学排序
题意
给定数组 
即对于位置 
思路
显然,如果对于一个数,有 
因此,我们直接枚举每个点即可。因为可能存在包含关系 
时间复杂度:
对应AC代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
#define int long long
vector<int> e[N][2];
int res[N], vis[N];
int dfs(int x, int t){
    if(vis[x]) return -1;
    vis[x] = true;
    int ans = 0;
    for(int each : e[x][t]){
        ans += dfs(each, t) + 1;
    }
    return ans;
}
signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int n, m;
    cin >> n >> m;
    for(int i=0;i<m;i++){
        int x, y;
        cin >> x >> y;
        e[x][0].emplace_back(y);
        e[y][1].emplace_back(x);
    }
    memset(res, -1, sizeof res);
    for(int i=1;i<=n;i++){
        memset(vis, 0, sizeof vis);
        int cntl = dfs(i, 1);
        vis[i] = false;
        int cntr = dfs(i, 0);
        if(cntl + cntr == n - 1) res[cntl + 1] = i;
    }
    for(int i=1;i<=n;i++) cout << res[i] << ' ';
    cout << '\n';
} 不该不做这题…
K. 清楚姐姐玩翻翻乐
待补充
L. 清楚姐姐的三角形I
题意
对于 
思路
显然,
时间复杂度:
对应AC代码
#include <bits/stdc++.h>
using namespace std;
const int N = 500010, inf = 0x3f3f3f3f;
#define int long long
signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t, va, vb, vc;
    cin >> t;
    while(t -- > 0){
        cin >> va >> vb >> vc;
        int la = vb + vc - va, lb = va + vc - vb, lc = va + vb - vc;
        if(la % 2 == 1 || lb % 2 == 1 || lc % 2 == 1){
            cout << "NO\n";
        }else{
            la /= 2;
            lb /= 2;
            lc /= 2;
            if(la + lb > lc && la + lc > lb && lb + lc > la){
                cout << "YES" << '\n' << la << ' ' << lb << ' ' << lc << '\n';
            }else cout << "NO\n";
        }
    }
} 怎么可以暴力呢
M. 清楚姐姐的三角形II
题意
给定数组长度 
思路
时间复杂度:
对应AC代码
#include <bits/stdc++.h>
using namespace std;
const int N = 500010, inf = 0x3f3f3f3f;
#define int long long
signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    for(int i=0;i<n/3;i++){
        cout << "1 1 2 ";
    }
    if(n % 3 == 1) cout << "1";
    if(n % 3 == 2) cout << "1 1";
} 差点斐波那契((
- 本文链接 https://floating-ocean.github.io/blog_old/posts/127583546/
 - 版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!