Codeforces 182 C. Optimal Sum

問題

数列a[i]が与えられる。
この数列のうち、最大k個の符号を好きに変えることができる。

このとき、連続するlen個の区間の和の絶対値| Σ[j = i to i + len - 1]a[j] | の最大値を求めよ。

制約条件

n≦10^5
a[i]≦10000
k≦n

方針

符号をマイナスからプラスに変えるだけを許したときの最大値をa[i]と、-a[i]について求めればよい。


これは、区間の中でマイナスのもののうち、小さいものから貪欲にk個マイナスの符号をつければよく、
集合のk番目の値を効率的に取り出す&集合の更新を効率的にやるには、平衡二分木などを使えば良い。

ソースコード

avl_treeはspaghetti sourceの。

int a[100000];

void run()
{
	int len, n, k;
	cin >> n >> len;
	rep(i, n) cin >> a[i];
	cin >> k;
	
	ll ans = -1ll << 60;
	rep(it, 2){
		avl_tree<int> tree;
		
		ll sum = 0;
		int sz = 0;
		rep(i, n){
			
			if(i >= len){
				sum -= a[i-len];
				if(k && a[i-len] < 0){
					if(sz < k || a[i-len] <= tree.rank(k-1)->key){
						sum += 2 * a[i-len];
						if(sz >= k+1) sum -= 2 * tree.rank(k)->key;
					}
					sz--;
					tree.erase(a[i-len]);
				}
			}
			
			sum += a[i];
			if(k && a[i] < 0){
				if(sz < k || a[i] <= tree.rank(k-1)->key){
					sum -= 2 * a[i];
					if(sz >= k) sum += 2 * tree.rank(k-1)->key;
				}
				sz++;
				tree.insert(a[i]);
			}
			if(i >= len - 1) ans = max(ans, sum);
		}
		if(it == 0) rep(i, n) a[i] *= -1;
	}
	cout << ans << endl;
}