CF纯思维题大汇总(一)

CF纯思维题大汇总(一)

前言

汇总第一部分来自题单:https://www.luogu.com.cn/training/2018#problems ,主要是三年前的题。大概断断续续做了两个礼拜,总体而说涉及到的算法很少,思路非常新颖,某些题也有多种不同的思路。适合给我们这种被模板化思维洗脑的老选手清理下脑子(。

因为 \(a_x\ge 2,a_y\ge 2\),并且 \(b_x\ge 0,b_y\ge 0\),所以关键点单调右下且非常稀疏。点的数量最多为 \(log_2 10^{16}\)。大概在 \(60\) 这个样子。因为点单调右下,任意两个点都能花费之间曼哈顿距离的时间互相到达。因为人的位置可能不在关键点。我们不妨先把视角放在在某个关键点如何在有限时间抵达尽可能多的关键点。假设关键词在中间,因为左上点比较稀疏,我们是不是应该 先尽量去到最左上角然后返回这个点再往下跑 ?这个贪心策略是正确的吗?考虑证明。对于某个关键点 \((X,Y)\) 的右下相邻点坐标为 \((a_x*X+b_x,a_y*Y+b_y)\)。因为 \(a_x\ge 2,a_y\ge 2,x_0\ge 1,y_0\ge 1,b_x\ge 0,b_y \ge 0\),所以 \((a_x-1)*X+b_x>X-x_0\)\((a_y-1)*Y+b_y>Y-y_0\)。这个式子把不等号左边的 \(X,Y\) 替换成右下方更大的 \(X',Y'\),不等号仍然成立。所以某个点到最左上角的点距离比其右下方的任意点到到其右下相邻点距离更小(*)。

对于最坏情况,左上角只有一个点,假设我们先去到左上方获取这个点,返回起点,再右下获取 \(k-1\) 个点将其与直接右下获取 \(k\) 个点做比较,第一种方案比第二种方案到第 \(k-1\) 个点多出一个返回的价值。由(*)得这个价值必定比右下第 \(k-1\) 个点到第 \(k\) 个点距离小。对于左上角有更多点的例子必定比最坏情况更优。贪心策略得证。

下一步由于我们起始不在关键点上,我们只要到了关键点就可以愉快地应用贪心策略了。一个暴力的做法是枚举起始点第一个抵达的关键点,因为关键词数量极少,\(n^2\) 大力枚举即可。思路清晰后实现难度极小。

#include <bits/stdc++.h>

#define int long long

using namespace std;

vector<pair<int, int> > q;

const int inf = 2e16;

int cal(int s, int t) {
    int res = 1;
    for (int i = 0; i < s; ++i) {
        if (t >= abs(q[s].first - q[i].first) + abs(q[s].second - q[i].second)) {
            t -= (abs(q[s].first - q[i].first) + abs(q[s].second - q[i].second)) * 2;
            res = s - i + 1;
            break;
        }
    }
    for (int i = q.size() - 1; i > s; --i) if (t >= abs(q[s].first - q[i].first) + abs(q[s].second - q[i].second))
        return i + 1;
    return res;
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int x0, y0, ax, ay, bx, by;
    cin >> x0 >> y0 >> ax >> ay >> bx >> by;
    int xs, ys, t;
    cin >> xs >> ys >> t;
    while (x0 <= inf && y0 <= inf) {
        q.push_back(make_pair(x0, y0));
        x0 = ax * x0 + bx, y0 = ay * y0 + by;
    }
    int ans = 0;
    for (int i = 0; i < q.size(); ++i) 
        if (abs(xs - q[i].first) + abs(ys - q[i].second) <= t)
            ans = max(ans, cal(i, t - abs(xs - q[i].first) - abs(ys - q[i].second)));
    cout << ans;
    return 0;
}
posted @ 2026-02-04 22:19  Jefferyzzzz  阅读(1)  评论(0)    收藏  举报