首页 > 其他分享 >题解 LGP7294【[USACO21JAN] Minimum Cost Paths P】/ accoders::NOI 5696【棋子】

题解 LGP7294【[USACO21JAN] Minimum Cost Paths P】/ accoders::NOI 5696【棋子】

时间:2023-12-18 17:34:25浏览次数:23  
标签:10 ch return NOI Paths int 题解 LL func

problem

Farmer John 的牧草地可以看作是一个\(N×M\)(\(2≤N≤10^9, 2≤M≤2⋅10^5\))的正方形方格组成的二维方阵(想象一个巨大的棋盘)。对于 \(x∈[1,N],y∈[1,M]\),从上往下第 \(x\) 行、从左往右第 \(y\) 列的方格记为 \((x,y)\)。此外,对于每一个 \(y∈[1,M]\),第 \(y\) 列拥有一个代价 \(c_y\)(\(1≤c_y≤10^9\))。

Bessie 从方格 \((1,1)\) 出发。如果她现在位于方格 \((x,y)\),则她可以执行以下操作之一:

  • 如果 \(y<M\),Bessie 可以以 \(x^2\) 的代价移动到下一列(\(y\) 增加一)。
  • 如果 \(x<N\),Bessie 可以以 \(c_y\) 的代价移动到下一行(\(x\) 增加一)。

给定 \(Q\)(\(1≤Q≤2⋅10^5\))个独立的询问,每个询问给定 \((x_i,y_i)\)(\(x_i∈[1,N],y_i∈[1,M]\)),计算 Bessie 从 \((1,1)\) 移动到 \((x_i,y_i)\) 的最小总代价。

solution \(c_2>c_3>c_4>\cdots>c_m\)

大概就是路径形如 \((1, 1)\to(p, 1)\to(p, y)\to(x, y)\),然后二次函数求个最小值。注意整型溢出。推荐一个 auto safemul = [&](LL x, LL y) { return x && y && bitclz(x) + bitclz(y) < 67 ? (LL)2e18 : x * y; }

solution

有 dp:

\[f(i, j) = \min(f(i - 1, j) + c[j], f(i, j - 1) + i ^ 2) \]

有点太简单了。

稍微跳跃一下,受到部分分的启发,我们考虑沿着 \(y\) 增加的方向扫描线,观察 dp 数组 \(f_y(x)\) 的变化:

  • 首先全部 \(x\) 会 \(f_y(x)=f_{y-1}(x)+x^2\)。
  • 然后逐个 \(x\geq 2\) 进行 \(f_y(x)\) 与 \(f_y(x-1)+c_y\) chkmin 的操作。
  • (注意特判 \(x=1\) 和 \(y=1\) 显然可以直接计算。)

因为这个 \(x^2\) 素质很差,但是它的差分 \(x^2-(x-1)^2=2x-1\) 素质还行,考虑对 \(f_y\) 做差分记为 \(g_y(x)=f_y(x)-f_y(x-1)\)(对 \(x=1\) 未定义)。这样,这个没有素质的 \(x^2\) 就变成了 \(g_y(x)=g_{y-1}(x)+2x-1\),是全局加等差数列。

进一步的我们发现这个 chkmin 的操作是很好做的。不妨假设 \(g_y\) 单调不减,归纳证明:\(x=1\) 是常函数 \(g_1(x)=c_y\),往后,首先 \(g_y(x)=g_{y-1}(x)+2x-1\),两个单调不减的函数相加,还是单调不减,然后从前往后考虑每个 \(g_y\),如果有一个 \(g_y(x)>c_y\),说明 \(f_y(x-1)+c_y<f_y(x)\),\(f_y(x)\) 的素质很差,可以取到 \(f_y(x-1)+c_y\),他今天必须取到这个值,直接把 \(g_y(x)\) 改为 \(c_y\);这一步 \(g\) 还是单调不减,因为从前往后。完了以后就说明了 \(g_y\) 是单调不减的。实际上 \(f_y\) 是下凸壳,chkmin 操作就是把斜率为 \(c_y\) 的直线切一刀凸壳,并把切掉的部分干掉改直。

于是我们考虑维护 \(g_y\),操作有:

  • 全局加 \(2x-1\)。
  • 全局 chkmin \(c_y\)。
  • 有性质 \(g_y\) 单调不减。

考虑使用珂朵莉树维护,每一段的 \(g\) 都是等差数列(或者说直线),全局加就是全局记一下,chkmin 就从尾端开始拿出一段和他 chkmin,做一个区间推平的操作。因为只有区间推平,所以复杂度是对的。

考虑询问是查询珂朵莉树上一段前缀的 \(g\) 的总和,使用普通平衡树维护。如果你想要细节就看看代码。

\(O(n\log n)\)。

code

// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define endl "\n"
#define debug(...) void(0)
#endif
typedef long long LL;
struct func {
    LL k, b;
    func operator+(const func& g) const { return { k + g.k, b + g.b }; }
    func& operator+=(const func& g) { return *this = *this + g; }
    LL operator()(LL x) { return k * x + b; }
    LL getsum(LL l, LL r) {
        auto& f = *this;
        return (f(l) + f(r)) * (r - l + 1) / 2;
    }
};
template <int N>
struct fhqtreap {
    func val[N + 10], tag[N + 10];
    LL sum[N + 10];
    int Ls[N + 10], Rs[N + 10], Lt[N + 10], Rt[N + 10];
    // L/R self, L/R subtree
    int ch[N + 10][2], tot, pri[N + 10];
    mt19937 rng{ random_device{}() };
    int newnode(int l, int r, func f) {
        int p = ++tot;
        pri[p] = rng();
        ch[p][0] = ch[p][1] = 0;
        Ls[p] = Lt[p] = l;
        Rs[p] = Rt[p] = r;
        val[p] = f;
        sum[p] = f.getsum(l, r);
        tag[p] = { 0, 0 };
        return p;
    }
    void maintain(int p) {
        sum[p] = sum[ch[p][0]] + sum[ch[p][1]] + val[p].getsum(Ls[p], Rs[p]);
        Lt[p] = ch[p][0] ? Lt[ch[p][0]] : Ls[p];
        Rt[p] = ch[p][1] ? Rt[ch[p][1]] : Rs[p];
    }
    void spread(int p, func f) {
        if (!p)
            return;
        val[p] += f, tag[p] += f, sum[p] += f.getsum(Lt[p], Rt[p]);
    }
    void pushdown(int p) { spread(ch[p][0], tag[p]), spread(ch[p][1], tag[p]), tag[p] = { 0, 0 }; }
    // 以 Ls 为关键字进行排序
    void split(int p, int v, int& x, int& y) {
        if (!p)
            return x = y = 0, void();
        pushdown(p);
        if (Ls[p] <= v)
            x = p, split(ch[p][1], v, ch[p][1], y);
        else
            split(ch[p][0], v, x, ch[p][0]), y = p;
        maintain(p);
    }
    int merge(int p, int q) {
        if (!p || !q)
            return p + q;
        pushdown(p);
        pushdown(q);
        if (pri[p] < pri[q]) {
            ch[p][1] = merge(ch[p][1], q);
            maintain(p);
            return p;
        } else {
            ch[q][0] = merge(p, ch[q][0]);
            maintain(q);
            return q;
        }
    }
    int getLast(int p) {
        pushdown(p);
        return ch[p][1] ? getLast(ch[p][1]) : p;
    }
    int root;
    void erase(int p) {
        int x, y, z;
        split(root, Ls[p] - 1, x, y);
        split(y, Ls[p], y, z);
        assert(y == p);
        root = merge(x, z);
    }
    void insert(int p) {
        int x, y;
        split(root, Ls[p] - 1, x, y);
        root = merge(x, merge(p, y));
    }
    LL find(int r, int p) {
        if (!p)
            return 0;
        pushdown(p);
        if (Rs[p] <= r)
            return sum[p] - sum[ch[p][1]] + find(r, ch[p][1]);
        else if (r < Ls[p])
            return find(r, ch[p][0]);
        else
            return sum[ch[p][0]] + val[p].getsum(Ls[p], r);
    }
    void dfs(int p) {
        if (!p)
            return;
        pushdown(p);
        dfs(ch[p][0]);
        debug("[%d, %d, y = %lldx + %lld], ", Ls[p], Rs[p], val[p].k, val[p].b);
        dfs(ch[p][1]);
    }
};
int n, m, q;
LL c[200010], ans[4000010];
LL d[200010];
vector<pair<int, int>> qry[200010];
fhqtreap<800010> t;
void ynycoding() {
    int& root = t.root = t.newnode(2, n, { 0, c[1] });
    // for (int i = 2; i <= n; i++) d[i] = c[1];
    for (int y = 2; y <= m; y++) {
        //  ++d[1]; // d[1] = y - 1
        //  for (int x = 2; x <= n; x++) d[x] = min(d[x] + 2 * x - 1, c[y]);
        t.spread(root, { 2, -1 });
        int last = n + 1;
        while (int p = t.getLast(root)) {
            int l = t.Ls[p], r = t.Rs[p];
            auto& f = t.val[p];
            if (f(r) <= c[y])
                break;
            else if (f(l) >= c[y])
                t.erase(p), last = t.Ls[p];
            else {
                int ans = l;
                for (int mid = (l + r) >> 1; l <= r; mid = (l + r) >> 1) {
                    if (f(mid) <= c[y])
                        ans = mid, l = mid + 1;
                    else
                        r = mid - 1;
                }
                t.erase(p);
                t.Rs[p] = ans;
                t.insert(p);
                last = ans + 1;
                break;
            }
        }
        if (last <= n)
            t.insert(t.newnode(last, n, { 0, c[y] }));
#ifdef LOCAL
        debug("y = %d\n", y);
        t.dfs(root);
        debug("\n");
#endif
        for (auto elem : qry[y]) {
            ans[elem.second] += t.find(elem.first, root);
            //    for (int i = 2; i <= elem.first; i++) ans[elem.second] += d[i];
        }
    }
}
int main() {
#ifndef LOCAL
    freopen("chess.in", "r", stdin);
    freopen("chess.out", "w", stdout);
    cin.tie(nullptr)->sync_with_stdio(false);
#endif
    cin >> n >> m;
    for (int i = 1; i <= m; i++) cin >> c[i];
    cin >> q;
    for (int i = 1; i <= q; i++) {
        int x, y;
        cin >> x >> y;
        if (x == 1)
            ans[i] = y - 1;
        else if (y != 1)
            qry[y].emplace_back(x, i), ans[i] = y - 1;
        else
            ans[i] = c[1] * (x - 1);
    }
    ynycoding();
    for (int i = 1; i <= q; i++) cout << ans[i] << endl;
    return 0;
}

标签:10,ch,return,NOI,Paths,int,题解,LL,func
From: https://www.cnblogs.com/caijianhong/p/solution-p7294.html

相关文章

  • JOISC2020题解
    \(\text{ByDaiRuiChen007}\)ContestLinkA.Building4ProblemLink题目大意给\(2n\)个数对\((a_i,b_i)\),构造一个非降序列\(c_i\)满足\(\forall1\lei\len,c_i\in\{a_i,b_i\}\),且\(c_i=a_i\)的位置恰好有\(n\)个。数据范围:\(n\le5\times10^5\)。思路......
  • boost beast http::read 一直阻塞不返回,问题解决, 使用parser对象的skip(true) 来解
    用beast作为客户端发送http请求后读web服务端返回的数据,遇到了http::read或http::async_read一直阻塞着,不返回,直到连接过期后被强制网络断开后read函数才返回。看了官方文档,文档里这么描述的,read要一直等到end_of_stream时才回退出阻塞状态。也就是连接失效后才行。但我们的......
  • 【POJ 2388】Who‘s in the Middle 题解(nth_element)
    描述FJ正在调查他的牛群,寻找最普通的奶牛。他想知道这头“中位数”奶牛产奶量:一半奶牛产奶的量与中位数相同或更多;一半的人给予同样多或更少。给定奇数头奶牛N(1<=N<10000)和它们的牛奶产量(1…1000000),求出所给牛奶的中位数,使至少一半奶牛所给的牛奶量相同或更多,至少一半奶牛的牛奶......
  • MySQL 8 手动安装后无法启动的问题解决
    首先的自我检讨与自我批评,最近有点懒,知识的更新慢,最近在更换系统到ubuntu22.04,废弃centos ,同时MYSQL都在8以上,之前MySQL都是在CENTOS7.5上安装,并且也都自动化安装,基本上没有问题,但到了ubuntu22.04基于对于系统的不熟悉,产生很多的问题。今天就梳理一下,转换了系统对于M......
  • 【题解】AtCoder agc065_a Shuffle and mod K
    传送门:https://atcoder.jp/contests/agc065/tasks/agc065_a 为了方便理解,我们把要求的东西乘一个$-1$,再把答案序列倒过来;也就是说,我们现在要求$min_{A'}^{A'为A的排列}(\sum_{i=1}^{N-1}((A_{i+1}-A_{i})$$mod$$K))$比较显然的是,如果我们确定了答案序列的第一个数是多......
  • [AGC016D] XOR Replace 题解
    题目链接点击打开链接题目解法很有思维难度的一道题首先考虑简化操作(或者说用一种比较好的方法表示)假设我们选择交换的位置为\(x\),不难发现,操作等价于交换\(sumxor\)和\(x\)于是,有解的条件就好判了,即\(\{b_i\}\subseteq\{a_i\}\bigcap\{x\}\)操作可以理解为路径,即我......
  • 题解 ABC333F【Bomb Game 2】
    来个可能有点麻烦但不用动脑子的暴力做法。直接设\(f_{i,j}\)表示有\(i\)个人时,第\(j\)个人幸存的概率。显然有\(f_{1,1}=1\)。对于\(i>1\),分类讨论容易得到:\[f_{i,j}=\begin{cases}\frac{f_{i,n}}{2},&j=1\\\frac{f_{i-1,j-1}+f_{i,j-1}}{2},&1<j\lei\\\e......
  • 链表面试题解析
    链表面试题解析1.删除链表中=val的所有节点/***Definitionforsingly-linkedlist.*publicclassListNode{*intval;*ListNodenext;*ListNode(){}*ListNode(intval){this.val=val;}*ListNode(intval,ListNodenext){......
  • BZOJ4403 序列统计 题解
    题目传送门前置知识排列组合|卢卡斯定理解法记\(m=r-l+1,0\lek\len-1\),枚举长度\(i\),等价于求\(\sum\limits_{j=1}^{m}x_j=i\)的非负整数解的数量。接着推式子就行。\(\begin{aligned}\sum\limits_{i=1}^{n}\dbinom{m+i-1}{i}\end{aligned}\)\(\begin{aligned......
  • 【电子公文系统】常见问题解答
    Q:如何在电子公文系统中创建新文档?A:登录系统后,点击“新建文档”按钮,选择相应的文档模板开始编辑。编辑完成后,可以选择保存草稿或提交审批。Q:我忘记了我的登录密码,应该怎么办?A:在登录界面点击“忘记密码”链接,根据提示输入你的电子邮箱或电话号码以接收重置密码的......