首页 > 其他分享 >AtCoder Grand Contest 049 E Increment Decrement

AtCoder Grand Contest 049 E Increment Decrement

时间:2023-07-19 21:46:19浏览次数:34  
标签:AtCoder limits Contest ll 斜率 最小值 maxn 049 multiset

洛谷传送门

AtCoder 传送门

好题。同时考查了 slope trick 和选手的计数能力,不愧是 AGC E。


先考虑问题的第一部分。

你现在有一个初始全为 \(0\) 的序列 \(b\)。你每次可以给 \(b\) 单点 \(\pm 1\),代价为 \(1\),或区间 \(\pm 1\),代价为 \(m\)。求把 \(b\) 变成给定序列 \(a\) 的最小代价。

考虑先执行区间加减操作,设操作完的序列为 \(c\),那么之后单点加减操作的代价就是 \(\sum\limits_{i = 1}^n |c_i - a_i|\)。对 \(c\) 差分,可以发现最少的区间操作次数就是差分后数组的正数项的值的和。所以总代价就是 \(\sum\limits_{i = 1}^n |c_i - a_i| + m \sum\limits_{i = 1}^n \max(c_i - c_{i - 1}, 0)\)。

考虑 dp,设 \(f_{i, j}\) 为 \(c_i = j\) 的最小代价(显然最优时 \(c_i \ge 0\))。初始有 \(\forall i \ge 0, f_{0, i} = mi\)。转移枚举 \(c_{i - 1}\),可得:

\[f_{i, j} = |a_i - j| + \min\limits_k f_{i - 1, k} + m \max(j - k, 0) \]

看到转移式里面有计算绝对值,联想到 slope trick。发现 \(f_i\) 是凸函数,并且每段的斜率在 \([-1, m + 1]\) 之间。容易归纳,我们初始的图像是一条斜率为 \(m\) 的射线,然后我们先进行 \(f_{i, j} \gets \min\limits_k f_{i - 1, k} + m \max(j - k, 0)\) 的更新,这个更新产生的影响就是,斜率为 \(-1\) 的段被拍平(斜率变成 \(0\)),斜率为 \(m + 1\) 的段斜率变成 \(m\)。然后我们给这个图像整体加上 \(|a_i - j|\) 的分段函数,也就是把 \(\le a_i\) 的段斜率减少 \(1\),\(> a_i\) 的段斜率增加 \(1\)。

考虑运用 slope trick,用 multiset 维护这个分段函数。回忆一下在 slope trick 中,我们维护的是分段函数图像变化的断点,并且一个断点代表斜率增加 \(1\)。那么在这题中,我们初始往 multiset 添加 \(m\) 个 \(0\) 代表断点为 \(0\) 且斜率为 \(m\),然后当 \(i = 1\) 时,因为图像不存在斜率为 \(-1\) 或 \(m + 1\) 的段,因此我们不需要进行删除操作,直接添加两个 \(a_i\) 表示 \(a_i\) 处斜率变化为 \(2\)。当 \(i > 1\) 时,我们先删除 multiset 中的最小值和最大值表示这两个断点被拍平了,不存在了,再添加两个 \(a_i\)。

至于统计答案,我们在每次添加两个 \(a_i\) 后统计,此时 multiset 中的最小值就是斜率 \(-1 \to 0\) 变化的断点,因此我们把答案累加 \(a_i - p\),其中 \(p\) 为 multiset 中的最小值(不用加绝对值是因为此时加入 \(a_i\) 后 \(p\) 一定 \(\le a_i\))。

于是我们现在可以 \(O(n \log n)\) 求解这个问题了。


考虑问题的第二部分,即统计所有可能的 \(a_i\) 对应的答案之和。

考虑我们上面的算法流程。

初始往 multiset 中添加 \(m\) 个 \(0\)。\(i = 1\) 时,往 multiset 中添加 \(2\) 个 \(a_i\),然后计算 \(a_i - p\),其中 \(p\) 为 multiset 中最小值;\(i > 1\) 时,先删除 multiset 中的最小值和最大值,然后往其中添加 \(2\) 个 \(a_i\),再计算 \(a_i - p\)。

\(a_i\) 部分的贡献系数是容易统计的,就是 \(K^{n - 1}\)(选定 \(a_i\) 后其他的可以任意选,都能产生贡献)。问题还剩下统计所有 \(p\) 的和。

我们枚举 \(nK\) 个可能的 \(p\),分别计算最小值 \(< p\) 和最小值 \(\le p\) 的方案数,二者差分一下就是 \(p\) 的贡献系数。

直接做不好维护 multiset,但是如果 \(a_i \in \{0, 1\}\),我们就能维护 \(1\) 的个数来表示整个 multiset 了(非常经典的套路:任意值转 \(01\))。我们不妨让 \(a_i \gets [a_i \ge p]\),这样最小值 \(< p\) 等价于最小值 \(= 0\)。

发现只有 \(1\) 的个数是 \(m + 2\) 时,multiset 中的最小值才不是 \(0\)。因此考虑一个容斥,总方案数减去最小值为 \(1\) 的方案数。总方案数显然是 \(nK^n\)(一共 \(n\) 轮,\(a\) 数组有 \(K^n\) 种产生方式),如果我们设 \(f_{i, j}\) 为进行到第 \(i\) 轮,multiset 中有 \(j\) 个 \(1\) 的方案数,那么最小值为 \(1\) 的方案数就是 \(\sum\limits_{i = 1}^n K^{n - i} f_{i, m + 2}\)(乘上 \(K^{n - i}\) 是因为第 \(i + 1 \sim n\) 轮中 \(a_i\) 的选择都不影响第 \(i\) 轮的最小值是 \(1\))。

现在考虑 \(f_{i - 1} \to f_i\)。对于一个 \(f_{i - 1, j}\),我们先进行删除操作,即 \(j \gets j - [j > 0] - [j = m + 2]\),然后我们考虑选择 \(a_i\),设 \(t = \sum\limits_{j = 1}^K [b_{i, j} \ge p]\),也就是能选的 \(1\) 的个数,那么 \(f_{i, j} \gets (K - t) f_{i - 1, j}\),\(f_{i, j + 2} \gets t f_{i - 1, j}\)。记得特判 \(i = 1\)。


至此我们终于以 \(O(n^3K)\) 的时间复杂度完成了这题。

code
// Problem: E - Increment Decrement
// Contest: AtCoder - AtCoder Grand Contest 049
// URL: https://atcoder.jp/contests/agc049/tasks/agc049_e
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 55;
const ll mod = 1000000007;

ll n, m, K, a[maxn][maxn], lsh[maxn * maxn], tot, pw[maxn], f[maxn][maxn];

inline void upd(ll &x, ll y) {
	x += y;
	(x >= mod) && (x -= mod);
}

inline ll calc(ll x) {
	mems(f, 0);
	ll cnt = 0;
	for (int i = 1; i <= K; ++i) {
		cnt += (a[1][i] >= x);
	}
	f[1][0] = K - cnt;
	f[1][2] = cnt;
	for (int i = 2; i <= n; ++i) {
		cnt = 0;
		for (int j = 1; j <= K; ++j) {
			cnt += (a[i][j] >= x);
		}
		for (int j = 0; j <= m + 2; ++j) {
			if (!f[i - 1][j]) {
				continue;
			}
			int nj = j - (j > 0) - (j == m + 2);
			upd(f[i][nj], f[i - 1][j] * (K - cnt) % mod);
			upd(f[i][nj + 2], f[i - 1][j] * cnt % mod);
		}
	}
	ll ans = n * pw[n] % mod;
	for (int i = 1; i <= n; ++i) {
		ans = (ans - f[i][m + 2] * pw[n - i] % mod + mod) % mod;
	}
	return ans;
}

void solve() {
	scanf("%lld%lld%lld", &n, &m, &K);
	pw[0] = 1;
	for (int i = 1; i <= n; ++i) {
		pw[i] = pw[i - 1] * K % mod;
	}
	ll ans = 0;
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= K; ++j) {
			scanf("%lld", &a[i][j]);
			lsh[++tot] = a[i][j];
			ans = (ans + a[i][j]) % mod;
		}
	}
	sort(lsh + 1, lsh + tot + 1);
	tot = unique(lsh + 1, lsh + tot + 1) - lsh - 1;
	ans = ans * pw[n - 1] % mod;
	for (int i = 1; i <= tot; ++i) {
		ans = (ans - lsh[i] * (calc(lsh[i] + 1) - calc(lsh[i]) + mod) % mod + mod) % mod;
	}
	printf("%lld\n", ans);
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

标签:AtCoder,limits,Contest,ll,斜率,最小值,maxn,049,multiset
From: https://www.cnblogs.com/zltzlt-blog/p/17566846.html

相关文章

  • 代码随想录算法训练营第三十三天| 1049. 最后一块石头的重量 II 494. 目标和 474.一
    1049.最后一块石头的重量II思路:因为含有两个石头的相撞,所以需要把dp的目标值改成sum/2,然后取得这个目标值的最大值,然后对sum-2*target代码:1//要求:有多个石头,两两撞击,取得剩下的石头的最小值2//——》一定要碰到最后一个3//注意:4//1,x==y:两个粉碎,x<y:y=......
  • AtCoder Beginner Contest 310
    A-OrderSomethingElse#include<bits/stdc++.h>usingnamespacestd;#defineintlonglongint32_tmain(){ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);intn,p,q;cin>>n>>p>>q;......
  • 【2023.07.14】Atcoder:past201912 - 第一回 アルゴリズム実技検定(div4+区域赛难度)过题
    G-Division解法一:位运算+状压枚举(赛时思路)范围显然,可以跑\(2^n\)的算法,考虑位运算状态压缩。以\(\mathcalO(2^n\cdot2^n)\)的复杂度分别枚举位于第一组、第二组中的人,随后计算每一种分组的快乐值,代码较长,赛时敲了半个小时,不过好在一发过了。总结:其实代码里面的剪枝完......
  • freee Programming Contest 2023(AtCoder Beginner Contest 310)
    Preface打的就是依托答辩,当时看一眼D感觉是个爆搜不想写就先跳了去想F,结果傻逼了没想出来最后30min了赶紧溜回去把D爆搜写了,但是已经罚时爆炸了,其实如果正常正序做的话排名会挺稳的后面一问包大爷发现F是个傻逼题,只能说计数水平实在是低下A-OrderSomethingElse签到题#i......
  • SMU Summer 2023 Contest Round 4
    SMUSummer2023ContestRound4A-TelephoneNumber思路:满足有8,且8后有大于等于11个数#include<bits/stdc++.h>usingnamespacestd;#defineintlonglongtypedefpair<int,int>PII;typedefpair<string,int>PSI;typedefpair<char,int>PCI;type......
  • SMU Summer 2023 Contest Round 4
    SMUSummer2023ContestRound4A.TelephoneNumber满足第一个8后面存在10个字符即可#include<bits/stdc++.h>#defineendl'\n'#defineintlonglongusingnamespacestd;intn,m;voidsolve(){cin>>n;strings;cin>>s;......
  • AtCoder Beginner Contest 310
    PeacefulTeams\(n\)个运动员,要分成\(t\)个队伍,一共有\(m\)对人不能放在一支队伍里,求方案数,每支队伍至少需要有一个人\(1\leqt\leqn\leq10\)题解:DFS搜索通过数据范围考虑爆搜我们考虑枚举的顺序\(O(n!)\)我们对每个人\(c\)枚举将其加到当前的\(d\)支队伍中新开一......
  • AtCoder Regular Contest 092 E Both Sides Merger
    洛谷传送门AtCoder传送门Keyobservation:每个元素的下标奇偶性不改变。于是讨论最后一个数是下标为奇数还是偶数加起来的数。将下标奇偶性相同的元素分开考虑。对于下标奇偶性相同的元素,不难发现答案的上界是所有\(>0\)的元素之和(没有\(>0\)的元素时选一个最大的),并且一......
  • freee Programming Contest 2023(AtCoder Beginner Contest 310)
    freeeProgrammingContest2023(AtCoderBeginnerContest310)-AtCoderA-OrderSomethingElse(atcoder.jp)题意是在买一道菜的情况下可以将原为\(P\)元的饮料优惠到\(Q\)元,否则就按原价买#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;signed......
  • AtCoder Beginner Contest 310
    (AtCoderBeginnerContest310) A-OrderSomethingElse思路:比较下打折和不打折的情况#include<bits/stdc++.h>usingnamespacestd;#defineintlonglongtypedefpair<int,int>PII;typedefpair<string,int>PSI;typedefpair<string,string>PSS;c......