Codeforces Round 939 D. Nene and the Mex Operator 【构造、思维、状压】

发布于:2024-04-26 ⋅ 阅读:(20) ⋅ 点赞:(0)

D. Nene and the Mex Operator

D

题意

给定一个长度为 n n n 的正整数数组 a a a,定义操作:

  • 选定一个区间 [ l , r ] [l, r] [l,r],将区间内的数字替换成: m e x ( a l , a l + 1 , . . . , a r ) mex(a_l,a_{l+1},...,a_r) mex(al,al+1,...,ar)

要求在不超过 5 ⋅ 1 0 5 5\cdot 10^5 5105 次操作下, 最大化数组的和

思路

首先需要注意到一个很关键的点:对于一个长度为 l e n len len 的区间 [ l , r ] [l,r] [l,r],不管怎样对这个区间操作,最终这个区间的每个数最大就是 l e n len len,排除掉 a i a_i ai 本身就比 l e n len len 大的情况
这是因为如果要产生 m e x = l e n mex = len mex=len,那么就需要 0 , 1 , . . . , l e n − 1 0,1,...,len - 1 0,1,...,len1 l e n len len 个数,再大的 m e x mex mex 值区间长度就不够放了

因此我们可以大胆猜测(对构造题很重要):一个长度为 l e n len len 的区间可以构造出 l e n len len a i = l e n a_i = len ai=len,也就是最后一次操作是 [ l , r ] [l,r] [l,r],且 m e x = l e n mex = len mex=len

如何操作?我们将区间移动到前缀 [ 1 , l e n ] [1,len] [1,len],就是对应代码 s o l v e ( k , p ) solve(k, p) solve(k,p) 函数, k = l e n k = len k=len p = l p = l p=l,表示将前 k k k 个数变为全 k k k 需要的操作

注意在每次进入 s o l v e solve solve 之前,我们需要保证区间全零
具体原理就是: a k = 0 a_k = 0 ak=0 不动,先递归求解 s o l v e ( k − 1 ) solve(k - 1) solve(k1),现在 [ 1 , k − 1 ] [1, k - 1] [1,k1] 全为 k − 1 k - 1 k1
然后我们将 [ 1 , k − 2 ] [1, k - 2] [1,k2] 设置为全零,再递归求解 s o l v e ( k − 2 ) solve(k -2) solve(k2),保持 a k − 1 = k − 1 a_{k - 1} = k - 1 ak1=k1 不变
一直循环下去,直到前缀为: 1 , 2 , 3 , . . . . , k − 1 , 0 1,2,3,...., k - 1,0 1,2,3,....,k1,0,这时 m e x mex mex 就等于 k k k

这样子递归操作的次数大约为 O ( 2 n ) O(2 ^ n) O(2n)
这是因为: s o l v e ( k ) solve(k) solve(k) 要调用一次 s o l v e ( k − 1 ) solve(k - 1) solve(k1),而后调用 s o l v e ( k − 2 ) → s o l v e ( 1 ) solve(k - 2) \rarr solve(1) solve(k2)solve(1)
s o l v e ( k − 1 ) solve(k -1) solve(k1) 也会调用 s o l v e ( k − 2 ) → s o l v e ( 1 ) solve(k - 2) \rarr solve(1) solve(k2)solve(1),因此 s o l v e ( k ) solve(k) solve(k) 大于是 s o l v e ( k − 1 ) solve(k - 1) solve(k1) 调用次数的 2 2 2 倍,也就是说,这是一个公比大约为 2 2 2 的等比数列

得出这个结论和构造方式后,我们就可以采用状压二进制枚举操作的区间,其余保留原值即可

// Problem: D. Nene and the Mex Operator
// Contest: Codeforces - Codeforces Round 939 (Div. 2)
// URL: https://codeforces.com/contest/1956/problem/D
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
#define fore(i,l,r)	for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n' 
#define ull unsigned long long

const int INF=0x3f3f3f3f;
const long long INFLL=0x3f3f3f3f3f3f3f3fLL;

typedef long long ll;

std::vector<std::pair<int, int>> opt;

void solve(int k, int p){
	if(k == 1){
		opt.push_back({p, p});
		return;
	}
	solve(k - 1, p);
	for(int i = k - 2; i > 0; --i){
		opt.push_back({p, p + i - 1}); //turn [1, i] to 0
		solve(i, p);
	}
	opt.push_back({p, p + k - 1});
}

int main(){
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    fore(i, 0, n)	std::cin >> a[i];
    int ans = 0;
    int sta = 0;
    fore(S, 0, 1 << n){
    	std::vector<bool> v(n);
    	fore(j, 0, n)	v[j] = (S >> j & 1);
    	int sum = 0;
    	int len = 0;
    	fore(i, 0, n){
    		if(!v[i]){
    			sum += len * len;
    			len = 0;
    			sum += a[i];
    		}
    		else ++len;
    	}
    	sum += len * len;
    	if(sum > ans){
    		ans = sum;
    		sta = S;
    	}
    }
    std::vector<std::pair<int, int>> vec;
	int i = 0;
	int l = -1;
	while(i < n){
		if(!(sta >> i & 1)){
			if(l != -1)	vec.push_back({l + 1, i});
			l = -1;
		}
		else if(l == -1) l = i;
		++i;
	}
	if(l != -1)	vec.push_back({l + 1, n});
	
	for(auto [l, r] : vec){
		fore(p, l, r + 1)
			if(a[p - 1])
				opt.push_back({p, p}); //turn a[p] to 0
		solve(r - l + 1, l);
	}
	
    std::cout << ans << ' ' << opt.size() << endl;
    for(auto [l, r] : opt)	std::cout << l << ' ' << r << endl;
	return 0; 
}