首页 > 其他分享 >Slope Trick 总结

Slope Trick 总结

时间:2024-02-16 14:23:26浏览次数:20  
标签:Slope 总结 return leftree ll Trick operator Modint friend

Slope Trick 总结

注意:Slope Trick 并不是斜率优化,斜率优化的英文是 Convex Hull Trick

算法适用性

Slope Trick 通常用于维护具有如下性质的函数:

  • 连续。
  • 是分段一次函数。
  • 是凸函数。
  • 每一段的斜率较小(通常为 \(O(n)\)),且均为整数。

常常用于优化动态规划。

不失一般性,约定本文中的函数均为下凸,且称以上所有性质为性质 \(P\)。

注意到,两个满足性质 \(P\) 的函数相加,得到的函数依然满足性质 \(P\)。

算法思想

维护在某个 \(x_0\) 处的 \(f(x_0),k_0\) 以及函数每个斜率变化点的集合。具体地,如果函数在 \(x\) 位置斜率增加了 \(\Delta k\),就在集合中插入 \(\Delta k\) 个 \(x\)。

性质 \(P\) 十分优美,它使得我们可以快速维护很多操作:

  • 相加:将 \(f(x_0),k_0\) 直接相加,斜率变化点的集合直接合并。常用于加一次函数、绝对值函数。
  • 取前缀/后缀 \(\min\):去掉 \(k<0\) 或 \(k>0\) 的部分。
  • 求 \(\min,\operatorname{argmin}\):提取 \(k=0\) 部分。
  • 平移:维护 \(f(x_0),k_0\) 变化,斜率变化点在全局打平移标记。
  • 翻转:维护 \(f(x_0),k_0\) 变化,斜率变化点在全局打翻转标记。

例题:P4597 序列 sequence

设 \(f_{i,j}\) 表示考虑 \([1,i]\) 的前缀,把最后一个数变为 \(j\) 的最小操作次数。显然有转移方程:

\[f_{i,j}=\min_{k\le j}\{f_{i-1,k}\}+|a_i-j| \]

设 \(F_i(j)=f_{i,j}\)。显然,\(F_i\) 可以由 \(F_{i-1}\) 先取前缀 \(\min\) 再加绝对值函数得到。所求即为 \(\min F_n\)。

由于 \(F_i\) 满足性质 \(P\),使用堆维护斜率变化点即可。

代码
//By: Luogu@rui_er(122461)
#include <bits/stdc++.h>
#define rep(x,y,z) for(ll x=y;x<=z;x++)
#define per(x,y,z) for(ll x=y;x>=z;x--)
#define debug printf("Running %s on line %d...\n",__FUNCTION__,__LINE__)
#define fileIO(s) do{freopen(s".in","r",stdin);freopen(s".out","w",stdout);}while(false)
using namespace std;
typedef long long ll;
const ll N = 5e5+5; 

ll n, a[N], ans;
priority_queue<ll> q;
template<typename T> void chkmin(T& x, T y) {if(x > y) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}

int main() {
	scanf("%lld", &n);
	rep(i, 1, n) {
		scanf("%lld", &a[i]);
		if(!q.empty() && q.top() > a[i]) {
			ans += q.top() - a[i];
			q.pop();
			q.push(a[i]);
		}
		q.push(a[i]);
	}
	printf("%lld\n", ans);
	return 0;
}

四倍经验:CF713C Sonya and Problem Wihtout a LegendCF13C SequenceP2893 [USACO08FEB] Making the Grade G
与该题几乎完全一致。

习题:LCP24 数字游戏

Slope Trick 简单题,也有对顶堆等做法。

习题:P3642 [APIO2016] 烟火表演

可并堆维护 Slope Trick。

代码
// Problem: P3642 [APIO2016] 烟火表演
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3642
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

//By: OIer rui_er
#include <bits/stdc++.h>
#define rep(x, y, z) for(int x = (y); x <= (z); ++x)
#define per(x, y, z) for(int x = (y); x >= (z); --x)
#define debug(format...) fprintf(stderr, format)
#define fileIO(s) do {freopen(s".in", "r", stdin); freopen(s".out", "w", stdout);} while(false)
#define endl '\n'
using namespace std;
typedef long long ll;

mt19937 rnd(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
int randint(int L, int R) {
    uniform_int_distribution<int> dist(L, R);
    return dist(rnd);
}

template<typename T> void chkmin(T& x, T y) {if(x > y) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}

template<int mod>
inline unsigned int down(unsigned int x) {
	return x >= mod ? x - mod : x;
}

template<int mod>
struct Modint {
	unsigned int x;
	Modint() = default;
	Modint(unsigned int x) : x(x) {}
	friend istream& operator>>(istream& in, Modint& a) {return in >> a.x;}
	friend ostream& operator<<(ostream& out, Modint a) {return out << a.x;}
	friend Modint operator+(Modint a, Modint b) {return down<mod>(a.x + b.x);}
	friend Modint operator-(Modint a, Modint b) {return down<mod>(a.x - b.x + mod);}
	friend Modint operator*(Modint a, Modint b) {return 1ULL * a.x * b.x % mod;}
	friend Modint operator/(Modint a, Modint b) {return a * ~b;}
	friend Modint operator^(Modint a, int b) {Modint ans = 1; for(; b; b >>= 1, a *= a) if(b & 1) ans *= a; return ans;}
	friend Modint operator~(Modint a) {return a ^ (mod - 2);}
	friend Modint operator-(Modint a) {return down<mod>(mod - a.x);}
	friend Modint& operator+=(Modint& a, Modint b) {return a = a + b;}
	friend Modint& operator-=(Modint& a, Modint b) {return a = a - b;}
	friend Modint& operator*=(Modint& a, Modint b) {return a = a * b;}
	friend Modint& operator/=(Modint& a, Modint b) {return a = a / b;}
	friend Modint& operator^=(Modint& a, int b) {return a = a ^ b;}
	friend Modint& operator++(Modint& a) {return a += 1;}
	friend Modint operator++(Modint& a, int) {Modint x = a; a += 1; return x;}
	friend Modint& operator--(Modint& a) {return a -= 1;}
	friend Modint operator--(Modint& a, int) {Modint x = a; a -= 1; return x;}
	friend bool operator==(Modint a, Modint b) {return a.x == b.x;}
	friend bool operator!=(Modint a, Modint b) {return !(a == b);}
};

const ll N = 6e5 + 5;

ll n, m, f[N], d[N], deg[N], ans;

struct LeftistTreeNode {
    ll lc, rc, val, dis;
    LeftistTreeNode(ll val = 0, ll dis = 0) : lc(0), rc(0), val(val), dis(dis) {}
};
struct LeftistTree {
    LeftistTreeNode t[N];
    ll rt[N], sz;
    ll newnode(ll val) {
        t[++sz] = LeftistTreeNode(val);
        return sz;
    }
    ll merge(ll u, ll v) {
        if(!u || !v) return u | v;
        if(t[u].val < t[v].val) swap(u, v);
        t[u].rc = merge(t[u].rc, v);
        if(t[t[u].lc].dis < t[t[u].rc].dis) swap(t[u].lc, t[u].rc);
        t[u].dis = t[t[u].rc].dis + 1;
        return u;
    }
    ll merge(ll u, ll v, ll args...) {
        return merge(merge(u, v), args);
    }
    ll seek(ll u) {
        return t[u].val;
    }
    void pop(ll& u) {
        u = merge(t[u].lc, t[u].rc);
    }
}leftree;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cin >> n >> m;
    rep(i, 2, n + m) cin >> f[i] >> d[i];
    rep(i, 2, n + m) ++deg[f[i]], ans += d[i];
    per(u, n + m, 2) {
        while(deg[u]-- > 1) leftree.pop(leftree.rt[u]);
        ll r = leftree.seek(leftree.rt[u]);
        leftree.pop(leftree.rt[u]);
        ll l = leftree.seek(leftree.rt[u]);
        leftree.pop(leftree.rt[u]);
        leftree.rt[u] = leftree.merge(leftree.rt[u], leftree.newnode(l + d[u]), leftree.newnode(r + d[u]));
        leftree.rt[f[u]] = leftree.merge(leftree.rt[f[u]], leftree.rt[u]);
    }
    while(deg[1]--) leftree.pop(leftree.rt[1]);
    while(leftree.rt[1]) {
        ans -= leftree.seek(leftree.rt[1]);
        leftree.pop(leftree.rt[1]);
    }
    cout << ans << endl;
    return 0;
}

标签:Slope,总结,return,leftree,ll,Trick,operator,Modint,friend
From: https://www.cnblogs.com/ruierqwq/p/18017106/slope-trick

相关文章

  • Codeforces Round 926 (Div. 2) 赛后总结
    这场比赛掉了前三场比赛上的分,望周知。SashaandtheBeautifulArray题目大意:一个有n个数的数组,对n个数进行排序,求数组中ai-ai-1(下标从2到n)的和的最大值。分析列出来和式,为an-an-1+an-1-an-2……-a1最后得到an-a1那么an最大,a1最小即可。很容易想到排序。#include<i......
  • 机器学习中的10种非线性降维技术对比总结
    降维意味着我们在不丢失太多信息的情况下减少数据集中的特征数量,降维算法属于无监督学习的范畴,用未标记的数据训练算法。尽管降维方法种类繁多,但它们都可以归为两大类:线性和非线性。线性方法将数据从高维空间线性投影到低维空间(因此称为线性投影)。例子包括PCA和LDA。非线性......
  • 省选游记+省选前总结
    Day-16初五的新年气息仍然有,不过接近尾声了,当热闹的气氛过后,便就只有无尽的孤独了中午便来到了二南,初中同学居然\(13\)天后才放假,真是秦始皇踩电线,赢麻了做题总结:P2619[国家集训队]TreeI\(wqs\)二分的板子题\(wqs\)主要利用了答案函数的凸性,通过斜率......
  • 每日总结
    Scala访问修饰符Scala访问修饰符基本和Java的一样,分别有:private,protected,public。如果没有指定访问修饰符,默认情况下,Scala对象的访问级别都是public。Scala中的private限定符,比Java更严格,在嵌套类情况下,外层类甚至不能访问被嵌套类的私有成员。私有(Private)成员......
  • 2024.2.15模拟赛总结
    这次发挥很好啊,rank1,300pts,比rank2高了70ptsT1发现操作二的影响是不可避免的,就尽可能让操作1没影响,每次就删连续的相同的数字,然后统计一下即可T2感觉思路很自然,首先只需要保留近k次操作如果有一个横的和一个竖的覆盖两个点,就可以直接走曼哈顿距离如果两点之间被横或......
  • 运算符总结
    publicclass基本运算符{publicstaticvoidmain(String[]args){//算术运算符//二元运算符inta=10;intb=20;intc=25;intd=25;System.out.println(a+b);System.out.println(a-b);......
  • 模拟赛总结
    2024.2.6【寒假集训】20240206测试T1珠子T2数组这个题,我没考虑到的是要保留全部的\(x,y\)操作信息,以及上一次\(A\)操作的时间等等。代码(参考lcy):#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;intn,m;intt1[500001],t2[500001];intlst[5......
  • C#实现异步编程的常用方式总结
    随着现代软件对性能和响应速度的要求越来越高,异步编程已经成为许多开发者必须掌握的技能。C#提供了多种实现异步编程的方式,每种方式都有其特定的适用场景和优缺点。本文将详细介绍C#中实现异步编程的常用方式,帮助读者更好地理解并选择合适的异步编程方法。一、Task和TaskC#......
  • slope trick
    slopetrick对于一类DP问题,DP状态是二维的\(f_{i,j}\),且\(f_i\)可以看作一个关于\(j\)的线性的连续凸分段函数转移时可以直接维护这个函数而优化复杂度维护操作以下凸函数为例我们维护第一段函数的斜率\(k\),用数据结构维护出斜率每变化\(1\)时的转折点的可重集注......
  • Codeforces Round 925 (Div. 3) 赛后总结
    此次总结借鉴了Register_int,0x3ea,幻想家协会会长的题解。感谢大佬。RecoveringaSmallString题目大意:将字母a-z编号为1-26,给出一个整数,此整数为三个字母之和,求改字符串的最小字典序。分析可以暴力循环,或者分情况讨论.我们只要尽力保持越前面的字母越小越好。#include<i......