1. 程式人生 > 其它 >[LOJ3192] 「ROI 2019 Day2」課桌

[LOJ3192] 「ROI 2019 Day2」課桌

噔噔蹬蹬~

前言

我收回上一篇題解的話,這題我自己做複雜度鐵定優化不下去,本地最多 \(94pts\)

但是沒有時間,只拿了 \(12pts\),虧大了。

題目

LOJ

講解

這題其實就利用一個東西:單調性。

我個人認為這題的結論應該比較顯然。

排除掉完全沒用的桌子(範圍被其它桌子完全包含),假如我們已經選好了桌子,將桌子按左端點排序後,每組的學生也應該按高度從低到高坐到座位上。

此時我們不再把學生分為 \(m\) 組,因為組內沒什麼聯絡,我們將坐在同一張桌子的同學視為一組,共有 \(2m\) 組,每組 \(n\) 個同學。

每組同學使用同一張桌子,這 \(2m\) 組使用的桌子按左端點單調不降。

然後我們直接對新定義的組分治,求出中間那個組的貢獻之後,由於桌子也是單調的,往兩邊分治就好了。

用飄飄蛋的話來說:實現的時候要精細一點,用 two-pointer 可以顯著降低複雜度。

時間複雜度 \(O((k+nm)\log_2n)\)

程式碼

不精細的實現
//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std;

typedef long long LL;
const int MAXN = 200005;
const LL INF = 1ll << 60;
int m,n,k;
int o[MAXN << 1]; 
bool tag[MAXN];
struct desk
{
	int l,r;
	bool operator < (const desk &px)const{
		if(l^px.l) return l < px.l;
		return r > px.r;
	}
}d[MAXN];
vector<desk> a[MAXN];

LL Read()
{
	LL x = 0,f = 1; char c = getchar();
	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
TT void Put1(T x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}

LL Get(desk stu,desk des)//student & desk
{
	LL ret = 0;
	if(stu.l < des.l) ret += des.l - stu.l;
	if(stu.l > des.r) ret += stu.l - des.r;
	if(stu.r < des.l) ret += des.l - stu.r;
	if(stu.r > des.r) ret += stu.r - des.r;
	return ret;
}
LL solve(int sl,int sr,int dl,int dr)//student lr,desk lr,好像是個暴力吧? LOJ有78 (Ofast)
{
	if(sl > sr) return 0;
	LL ret = 0;
	if(dl == dr)
	{
		for(int i = 1;i <= m;++ i)
			for(int j = sl;j <= sr;++ j)
				ret += Get(a[i][j],d[dl]);
		return ret;
	}
	int mid = (sl+sr) >> 1,B = dl; LL MIN = INF;
	for(int i = dl;i <= dr;++ i)
	{
		LL cur = 0;
		for(int j = 1;j <= m;++ j) cur += Get(a[j][mid],d[i]);
		if(cur < MIN) B = i,MIN = cur;
	}
	return MIN+solve(sl,mid-1,dl,B)+solve(mid+1,sr,B,dr);
}

int main()
{
//	freopen("party.in","r",stdin);
//	freopen("party.out","w",stdout);
	m = Read(); n = Read(); k = Read();
	for(int i = 1;i <= k;++ i) d[i].l = Read(),d[i].r = Read();
	sort(d+1,d+k+1);
	int R = 0;
	for(int i = 1;i <= k;++ i)
	{
		if(R >= d[i].r) tag[i] = 1;
		R = Max(R,d[i].r);
	}
	int kk = k; k = 0;
	for(int i = 1;i <= kk;++ i) if(!tag[i]) d[++k] = d[i];
	for(int i = 1;i <= m;++ i)
	{
		for(int j = 0;j < (n<<1);++ j) o[j] = Read();
		sort(o,o+(n<<1));
		for(int j = 0;j < (n<<1);j += 2) a[i].emplace_back(desk{o[j],o[j+1]});
	}
	Put(solve(0,n-1,1,k),'\n');
	return 0;
}
精細的實現
//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std;

typedef long long LL;
const int MAXN = 200005;
const LL INF = 1ll << 60;
int m,n,k;
int o[MAXN << 1]; 
bool tag[MAXN];
struct desk
{
	int l,r;
	bool operator < (const desk &px)const{
		if(l^px.l) return l < px.l;
		return r > px.r;
	}
}d[MAXN];
vector<desk> a[MAXN];
vector<int> fs[MAXN];//final students list

LL Read()
{
	LL x = 0,f = 1; char c = getchar();
	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
TT void Put1(T x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}

LL Get(int h,desk des)//student & desk
{
	if(h < des.l) return des.l - h;
	if(h > des.r) return h - des.r;
	return 0;
}
LL pre[MAXN << 1];
LL Query(int l,int r)
{
	if(l > r) return 0;
	LL ret = pre[r];
	if(!l) return ret;
	return ret - pre[l-1];
}
LL solve(int sl,int sr,int dl,int dr)//student lr,desk lr,O((k+nm)log_2n)
{
	if(sl > sr) return 0;
	LL cur = 0,MIN = INF;
	int lid = 0,rid = 0,B = dl,mid = (sl+sr) >> 1;
	for(int i = 0;i < (m<<1);++ i) pre[i] = ((i ? pre[i-1] : 0) + fs[mid][i]);
	for(int i = dl;i <= dr;++ i)
	{
		while(lid < (m<<1) && fs[mid][lid] < d[i].l) ++lid;
		while(rid < (m<<1) && fs[mid][rid] <= d[i].r) ++rid;
		cur = 1ll * d[i].l * lid - Query(0,lid-1) + Query(rid,(m<<1)-1) - 1ll * ((m<<1)-rid) * d[i].r;
		if(cur < MIN) MIN = cur,B = i;
	}
	return MIN+solve(sl,mid-1,dl,B)+solve(mid+1,sr,B,dr);
}

int main()
{
//	freopen("party.in","r",stdin);
//	freopen("party.out","w",stdout);
	m = Read(); n = Read(); k = Read();
	for(int i = 1;i <= k;++ i) d[i].l = Read(),d[i].r = Read();
	sort(d+1,d+k+1);
	int R = 0;
	for(int i = 1;i <= k;++ i)
	{
		if(R >= d[i].r) tag[i] = 1;
		R = Max(R,d[i].r);
	}
	int kk = k; k = 0;
	for(int i = 1;i <= kk;++ i) if(!tag[i]) d[++k] = d[i];
	for(int i = 1;i <= m;++ i)
	{
		for(int j = 0;j < (n<<1);++ j) o[j] = Read();
		sort(o,o+(n<<1));
		for(int j = 0;j < (n<<1);j += 2) fs[j >> 1].emplace_back(o[j]),fs[j >> 1].emplace_back(o[j+1]);
	}
	for(int i = 0;i < n;++ i) sort(fs[i].begin(),fs[i].end());
	Put(solve(0,n-1,1,k),'\n');
	return 0;
}