1. 程式人生 > 其它 >I、Identical Day from 第二屆“聯想杯”

I、Identical Day from 第二屆“聯想杯”

貪心的策略有些問題,如果用堆維護最長段對半分顯然不會是最優的(儘量均分成3段比中間切一刀加上左1/4切一刀更優)

正確的貪心策略是堆維護把一段均分成x段的變成均分成x+1段可以減少多少權值,每次取最大肯定是最優的。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
#define pii pair<int,int>
#define pll pair<ll,ll>
const double PI = acos(-1);
const int inf = 1e9 + 7;
const ll lnf = 1e18 + 7;
const int maxn = 2e5 + 5;
ll mod = 1e9 + 7;


//不應該去貪心對半分,而是去貪心每次取可消除差值最大

ll f(ll x)
{
	return x * (x + 1) / 2;
}

ll cut(ll x, ll n)//把x長的1分成n+1份
{
	if (n == x)
		return 0;
	x -= n;//中間要切n刀,分成n+1份,每份長x/(n+1)
	n++;
	if (x % n == 0)//剛好均分
		return n * f(x / n);
	ll cha = x % n;
	return cha * f(x / n + 1) + (n - cha) * f(x / n);
}

struct node {
	ll a, b, c;
	friend bool operator<(node x, node y)
	{
		return x.a < y.a;
	}
};

priority_queue<node>q;

int main()
{
	fastio;
	ll n, k;
	cin >> n >> k;
	string s;
	cin >> s;
	ll sum = 0;
	ll now = 0;
	for (int i = 0; i < s.length(); i++)
	{
		if (s[i] == '0')
		{
			if (sum)
			{
				now += f(sum);
				q.push({ f(sum) - cut(sum,1),1,sum }), sum = 0;
			}
		}
		else
			sum++;
	}
	if (sum)
	{
		now += f(sum);
		q.push({ f(sum) - cut(sum,1),1,sum }), sum = 0;
	}
	ll ans = 0;
	while (now > k && !q.empty())
	{
		ll cha = q.top().a;
		n = q.top().b, sum = q.top().c;
		q.pop();
		//cout << cha << endl;
		ans++;
		now -= cha;
		if (n < sum)
			q.push({ cut(sum, n) - cut(sum, n + 1),  n + 1,sum  });
	}
	cout << ans;
	return 0;
}