15年國賽 居民集會

標題:居民集會

藍橋村的居民都生活在一條公路的邊上,公路的長度爲L,每戶家庭的位置都用這戶家庭到公路的起點的距離來計算,第i戶家庭距起點的距離爲di。

每年,藍橋村都要舉行一次集會。今年,由於村裏的人口太多,村委會決定要在4個地方舉行集會,其中3個位於公路中間,1個位最公路的終點。

已知每戶家庭都會向着遠離公路起點的方向去參加集會,參加集會的路程開銷爲家庭內的人數ti與距離的乘積。

給定每戶家庭的位置di和人數ti,請爲村委會尋找最好的集會舉辦地:p1, p2, p3, p4 (p1<=p2<=p3<=p4=L),使得村內所有人的路程開銷和最小。

【輸入格式】
輸入的第一行包含兩個整數n, L,分別表示藍橋村的家庭數和公路長度。
接下來n行,每行兩個整數di, ti,分別表示第i戶家庭距離公路起點的距離和家庭中的人數。

【輸出格式】
輸出一行,包含一個整數,表示村內所有人路程的開銷和。
【樣例輸入】
6 10
1 3
2 2
4 5
5 20
6 5
8 7
【樣例輸出】
18
【樣例說明】
在距起點2, 5, 8, 10這4個地方集會,6個家庭需要的走的距離分別爲1, 0, 1, 0, 2, 0,總的路程開銷爲1*3+0*2+1*5+0*20+2*5+0*7=18。

【數據規模與約定】
對於10%的評測數據,1<=n<=300。
對於30%的評測數據,1<=n<=2000,1<=L<=10000,0<=di<=L,di<=di+1,0<=ti<=20。
對於100%的評測數據,1<=n<=100000,1<=L<=1000000,0<=di<=L,di<=di+1,0<=ti<=1000000。

要在一條線上找到4個點(3箇中間點加1個端點)來使得其他點到這些點的距離與權值乘積最小。
使用枚舉的方法可以枚舉3箇中間點,這樣的複雜度爲O(n^4),使用前綴和可以優化到O(n^3)。。
使用動態規劃,令f[i][j]表示在前i個地方建j個端點的最小答案。
則f[i][j]=f[k][j-1]+cost(i+1,j)。這種方法需要枚舉k,複雜度爲O(n^2)
要想獲得更多的分,需要對動態規劃進行優化。
這裏考慮i是第二個選擇的點的時候它的前一個點j的取值,通過列出公式可知j的選擇相對於i的遞增是遞增的,而且滿足斜率優化的性質,可以維護一個下凸序列通過單調隊列來優化,在O(n)的時間內求出第二個選擇的點是i時前面一斷的最優值f[i]。
通過類似的方法可以求出當第二個選擇的點是i時後面一斷的最優值g[i],這裏需要推導不同的公式,得到不同的斜率優化的結果,但基本思路和推導過程相似。將f[i]+g[i]取個最小值即可得到答案。
這種方法推導具有較高難度,在實現過程中,由於數據範圍特別大,雖然答案在long long範圍內,但對於斜率優化的判斷中,即使使用long long也會出現越界情況,需要考慮到一點,使用double或者高精度計算來解決越界問題。

 

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>

using namespace std;

const int MAXN = 100100;
int n, L;
long long d[MAXN], t[MAXN];
long long s[MAXN], sd[MAXN], f[MAXN], g[MAXN];
int sta[MAXN], top;
long long ans;

void init()
{
	scanf("%d%d", &n, &L);
	for (int i = 1; i <= n; ++i)
	{
		int x, y;
		scanf("%d%d", &x, &y);
		d[i] = x;
		t[i] = y;
	}
	++n;
	d[n] = L;
	t[n] = 0;
	
	
	int j = 1;
	for (int i = 2; i <= n; ++i)
	{
		if (d[i]==d[j])
			t[j] += t[i];
		else
		{
			++j;
			d[j] = d[i];
			t[j] = t[i];
		}
	}
	n = j;
}

class BigNumber {
public:
	int l, sig;
	int s[100];
	BigNumber(long long x)
	{
		if (x < 0)
		{
			sig = -1;
			x = -x;
		}
		else
			sig = 1;
		memset(s, 0, sizeof(s));
		l = 0;
		while (x>0LL)
		{
			s[l++] = x%10;
			x /= 10;
		}
		if (l==0)
			l = 1;
	}
	BigNumber operator *(const BigNumber &r) const {
		BigNumber ret(0LL);
		ret.sig = sig * r.sig;
		ret.l = l + r.l - 1;
		for (int i = 0; i < l; ++i)
			for (int j = 0; j < r.l; ++j)
				ret.s[i+j] += s[i] * r.s[j];
		for (int i = 0; i < ret.l; ++i)
		{
			ret.s[i+1] += ret.s[i] / 10;
			ret.s[i] %= 10;
		}
		if (ret.s[ret.l])
			++ret.l;
		while (ret.l>1 && ret.s[ret.l-1]==0)
			--ret.l;
		if (ret.l==1 && ret.s[0]==0)
			ret.sig = 1;
		return ret;
	}
	bool operator <(const BigNumber &r) const {
		if (sig != r.sig) return sig < r.sig;
		if (l != r.l) return (l < r.l) ^ (sig == -1);
		for (int i = l - 1; i >= 0; --i)
			if (s[i] != r.s[i])
				return (s[i] < r.s[i]) ^ (sig == -1);
		return false;
	}
};


void process()
{
	long long oo = 100000LL * 1000000LL * 1000000LL + 1;
	s[0] = 0;
	sd[0] = 0;
	for (int i = 1; i <= n; ++i)
	{
		s[i] = s[i-1] + t[i];
		sd[i] = s[i] * d[i];
	}
	sta[0] = 0;
	top = 1;
	int best = 0;
	for (int i = 1; i <= n; ++i)
	{
		while (best+1<top && d[i]*(s[sta[best+1]]-s[sta[best]])>=(sd[sta[best+1]]-sd[sta[best]]))
			++best;
		f[i] = s[i] * d[i] - s[sta[best]] * (d[i] - d[sta[best]]);
		while (top>1 && BigNumber(sd[i]-sd[sta[top-1]])*BigNumber(s[sta[top-1]]-s[sta[top-2]])<BigNumber(sd[sta[top-1]]-sd[sta[top-2]])*BigNumber(s[i]-s[sta[top-1]]))
			--top;
		sta[top++] = i;
		if (best>=top)
			best = top-1;
	}
	for (int i = 1; i <= n; ++i)
		sd[i] = s[i] * (d[n]-d[i]);
	sta[0] = n;
	g[n] = 0;
	top = 1;
	best = 0;
	for (int i = n - 1; i >= 1; --i)
	{
		while (best+1<top && s[i]*(d[sta[best]]-d[sta[best+1]])<=-(sd[sta[best]]-sd[sta[best+1]]))
		{
			++best;
		}
		g[i] = (s[sta[best]]-s[i]) * d[sta[best]] + (s[n]-s[sta[best]]) * d[n];
		while (top>1 && BigNumber(sd[i]-sd[sta[top-1]])*BigNumber(d[sta[top-1]]-d[sta[top-2]])<BigNumber(sd[sta[top-1]]-sd[sta[top-2]])*BigNumber(d[i]-d[sta[top-1]]))
			--top;
		sta[top++] = i;
		if (best>=top)
			best = top-1;
	}
	ans = oo;
	for (int i = 1; i <= n; ++i)
		if (f[i] + g[i] < ans)
			ans = f[i] + g[i];
	long long tmp = 0;
	for (int i = 1; i <= n; ++i)
		ans -= (t[i]+0LL) * d[i];
		
}

void print()
{
	cout << ans << endl;
}

int main()
{
	init();
	process();
	print();
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章