首页 > 其他分享 >Solution - Partition Game

Solution - Partition Game

时间:2023-11-17 11:22:58浏览次数:39  
标签:pre 下标 int Partition Solution pos Game ans dp

Link.

做 vjudge 的题有一种美丽的窒息的感觉。

设 \(f_{i, j}\) 表示前 \(i\) 个选 \(j\) 段出来的最小代价,转移 \(f_{i, j} = \min_{0 \leq k < i} \{f_{k, j - 1} + w_{k + 1, i} \}\),\(w_{k + 1, i}\) 是 \([k + 1, i]\) 这一段的代价,时间复杂度 \(O(n^2k)\),然后就不会做了 /ll

观察一下 \(f_{i - 1, j} = \min_{0 \leq k < i - 1} \{f_{k, j - 1} + w_{k + 1, i - 1} \}\),转移到 \(f_{i, j}\) 首先多了一个 \(f_{i - 1, j - 1} + w_{i - 1 + 1, i} = f_{i - 1, j - 1}\),可以直接 \(O(1)\) 维护。然后把 \(f_{k, j - 1} + w_{k + 1, i - 1}\) 这一坨看成一个下标为 \(k\) 的序列,转移 \(w_{k + 1, i - 1} \to w_{k + 1, i}\) 只需要考虑 \(a_i\) 的贡献,这个只对 \(a_i\) 这个值的位置有影响,其它的没有影响(反正不涉及到这个值,\(first\) 和 \(last\) 不变),那么有影响的部分就是数组中的 \([1, pre_i]\),其中 \(pre_i\) 表示 \(i\) 前面第一个和 \(a_i\) 相等的下标,因为 \(pre_i\) 之后是没有和 \(a_i\) 相等的值的。对于这个部分,\(last(a_i)\) 从 \(pre_i\) 变成了 \(i\),增加量是 \(i - pre_i\)。

把上面这一坨抽象一下,就是维护一个区间加和区间查询最小值的玩意,直接线段树维护 \(O(n \log n)\)。

然后 \(f_{i, j}\) 只和 \(f_{i, j - 1}\) 有关,可以缩掉一维,外层循环 \(j\),每次计算 \(f_i\) 即可。总的时间复杂度 \(O(k n \log n)\)。

一个小细节:线段树序列下标为 \(k\) 对应的是 \(w_{k + 1, i - 1}\),要和数组下标对应,实际操作的时候操作的下标是 \([0, pre_i - 1]\)。

namespace liuzimingc {
const int N = 35005, M = 105;
#define endl '\n'

int n, k, a[N], dp[N], pre[N], pos[N];
struct segment_tree {
	int l, r, val, tag;
} t[N << 2];

void push_down(int p) {
	if (t[p].tag) {
		t[p << 1].tag += t[p].tag;
		t[p << 1].val += t[p].tag;
		t[p << 1 | 1].tag += t[p].tag;
		t[p << 1 | 1].val += t[p].tag;
		t[p].tag = 0;
	}
}

void push_up(int p) {
	t[p].val = min(t[p << 1].val, t[p << 1 | 1].val);
}

void build(int p, int l, int r) {
	t[p].l = l, t[p].r = r, t[p].tag = 0;
	if (l == r) {
		t[p].val = dp[l];
		return;
	}
	int mid = l + r >> 1;
	build(p << 1, l, mid);
	build(p << 1 | 1, mid + 1, r);
	push_up(p);
}

void update(int p, int l, int r, int v) {
	if (l <= t[p].l && t[p].r <= r) {
		t[p].val += v;
		t[p].tag += v;
		return;
	}
	push_down(p);
	int mid = t[p].l + t[p].r >> 1;
	if (l <= mid) update(p << 1, l, r, v);
	if (r > mid) update(p << 1 | 1, l, r, v);
	push_up(p);
}

int ask(int p, int l, int r) {
	if (l <= t[p].l && t[p].r <= r) return t[p].val;
	push_down(p);
	int mid = t[p].l + t[p].r >> 1, ans = 0x3f3f3f3f;
	if (l <= mid) ans = min(ans, ask(p << 1, l, r));
	if (r > mid) ans = min(ans, ask(p << 1 | 1, l, r));
	return ans;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	cin >> n >> k;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		pre[i] = pos[a[i]];
		pos[a[i]] = i;
	}
	memset(dp, 0x3f, sizeof(dp)); // 现在代码中的 dp 数组随机写 dp / f
	dp[0] = 0;
	for (int j = 1; j <= k; j++) {
		build(1, 0, n);
		for (int i = 1; i <= n; i++) {
            dp[i] = dp[i - 1];
			if (pre[i]) update(1, 0, pre[i] - 1, i - pre[i]);
			dp[i] = ask(1, 0, i - 1);
		}
	}
	cout << dp[n] << endl;
	return 0;
}
} // namespace liuzimingc

标签:pre,下标,int,Partition,Solution,pos,Game,ans,dp
From: https://www.cnblogs.com/liuzimingc/p/partition.html

相关文章

  • 【题解 CF1628D2】 Game on Sum
    GameonSum(HardVersion)题面翻译Alice和Bob正在玩一个游戏,游戏分为\(n\)个回合,Alice和Bob要轮流对一个数\(x\)进行操作,已知这个数初始值是\(0\)。具体每个回合的行动规则如下:Alice选择一个在区间\([0,k]\)之间的实数\(t\)。Bob可以选择让\(x\)变成\(......
  • Databend 源码阅读: Storage 概况和 Read Partitions
    作者:张祖前DatabendLabs成员,数据库研发工程师https://github.com/zhyass❤️ 友情提示:代码演进较快,请注意文档的时效性哦!引言Databend将存储引擎抽象成一个名为Table的接口,源码位于query/catalog/src/table.rs。Table接口定义了read、append、alter、optimize、tr......
  • Solution - Hossam and (sub-)palindromic tree
    又名:《最近vjudge题全部罚坐》。唯一Trick:回文序列,就想区间dp!时间复杂度\(O(n^2)\)!如果是序列:\(f_{l,r}\)表示\([l,r]\)的最长回文子序列,\(f_{l,r}=\max(f_{l+1,r},f_{l,r-1},f_{l+1,r-1}+[s_l=s_r])\),\(s\)是字符串。很trivial。但是这是在......
  • Solution Set - CF787
    ViveleR&M!还被种草了Hurt,真的颇有感触,但这是SolutionSet,就不写了。A.TheMonsterexgcd,但是发现\(1\leqa,b,c,d\leq100\)直接暴力枚举即可。我认为这是\(O(1)\)的,但题解认为是\(O(n)\),感觉不如原神。B.NotAfraid每一组里面只要有来自同一个宇宙的Rick......
  • Solution - 公路旅行
    Link。又名:《不会T1记》。原来用到了神秘的倍增!但是我写了一个申必二分,最坏\(O(qn\logn)\),甚至不如暴力,我是......
  • CodeForces 1895E Infinite Card Game
    洛谷传送门CF传送门容易转化成经典的有向图博弈模型。每张牌建一个点,若\(x\)能打败\(y\)就连一条\(x\toy\)的边。入度为\(0\)的点为必败态,之后类似拓扑排序倒推即可。具体就是若存在边\(u\tov\),若\(u\)为必败态则\(v\)为必胜态并加入队列;若\(v\)的所有前驱......
  • 「模拟赛」Solution Set
    \(\text{heart}\)\(\text{Solution}\)可以记\(f(u)\)为从\(u\)出发到某个点停止的方案数,\(f(u)\)可以\(O(n)\)转移,显然复杂度为\(O(n^2)\).当前我们要转移\(u\)子树内,对于\(v\in\text{subtree(u)}\)我们记\(g_v\)为\(\min\limits_{p_k>p_j}p_k\),其中\(k\)在......
  • Solution - Makoto and a Blackboard
    Link。朴素dp应该不用说了。放个用map的代码。intdfs(intn,intk){ if(!k)returnn; if(f[make_pair(n,k)])returnf[make_pair(n,k)]; inttot=0,ans=0; for(inti=1;i*i<=n;i++){ if(n%i)continue; ans=(ans+dfs(i,k-1))%M......
  • 公告 & Solution - 公路旅行
    以后应该会用Obsidian搭个博客,博客园可能会被弃用了。为了有点价值放个不知道什么东西上来。Link。不会T1!原来用到了神秘的倍增!但是我写了一个申必二分,最坏\(O(qn\logn)\),甚至不如暴力,我是......
  • CF467B Fedor and New Game
    前言传送门本题思维难度:橙。本题代码难度:橙或红。综合难度:橙。本人代码码量位居第二,但是呢,我的空格多,所以,还不来看一下?题意根据题目,若两人一人有$j$,一人没$j$,则异或后,第$j$位为$1$。那么,题目转化为:已知有$m+1$个数,求出满足$a_i$异或$a_{m+1}$结果的$1$的......