[HDU6800] Play osu! on Your Tablet

前言

有意思的 dp。

题目

HDU

VJudge

讲解

\(f(i,j)\) 表示两个序列末尾是 \(i,j\) 的最短距离。

如果 \(j<i-1,f(i,j)=f(i-1,j)+dis(i,i-1)\),很显然嘛,不然它就转移到 \(f(i-1,i)\) 去了。

所以我们只需要单独考虑 \(f(i,i-1)\) 的转移!

\(g(i)=f(i,i-1)\)\(g(i)=\min\{f(i-1,j)+dis(i,j)\}(j\in[0,i-2])\)

\(ans=\min\{\min\{f(n,i)\},g(n)\}(i\in[1,n-2])\)

我们令 \(s(n)=\underset{i=2}{\overset{n}{\sum}} dis(i,i-1)\),则有:

\(ans=\min\{g(i)+s(n)-s(i)\}(i\in[1,n])\)

真不戳,我们把 \(f\) 给搞掉了,当然我们也可以把 \(g(i)\) 的转移换一下:

\(g(i)=\min\{g(j)+s(i-1)-s(j)+dis(j-1,i)\}=\min\{g(j)-s(j)+dis(j-1,i)\}+s(i-1)(j\in[0,i-1])\)

我们发现除了 \(dis(j-1,i)\) ,其它的都很好算出来,所以我们考虑把 \(dis(j-1,i)\) 拆出来,分四类讨论。

显然这个 dp 可以用 \(\operatorname{cdq}\)分治 优化,其中用树状数组求前缀最值,注意不要忘了后面有一项 \(s(i-1)\)

时间复杂度为 \(O(nlog^2n)\)

代码

//12252024832524
#include <cstdio>
#include <cstring>
#include <algorithm>
#define TT template<typename T>
using namespace std; 

typedef long long LL;
const int MAXN = 100005;
const LL INF = (1ll << 60);//抱歉0x(3f)^8
int n;
LL g[MAXN],h[MAXN],s[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;}

struct BIT
{
	LL b[MAXN];
	int lowbit(int x){return (x & -x);}
	void Add(int x,int len,LL val){for(int i = x;i <= len;i += lowbit(i)) b[i] = Min(b[i],val);}
	LL Query(int x){LL ret = INF;for(int i = x;i >= 1;i -= lowbit(i)) ret = Min(ret,b[i]);return ret;}
	void init(int x){for(int i = 1;i <= x;++ i) b[i] = INF;}
}pre,suf; 

struct node
{
	int x,y,px,py,ID,lsh;
}p[MAXN],q[MAXN];
bool cmpx1(node a,node b){if(a.px != b.px) return a.px < b.px; return a.ID < b.ID;}
bool cmpx2(node a,node b){if(a.px != b.px) return a.px > b.px; return a.ID < b.ID;}
bool cmpy(node a,node b){if(a.py != b.py) return a.py < b.py; return a.ID < b.ID;}
bool cmpID(node a,node b){return a.ID < b.ID;}
int dis(int x,int y){return Abs(p[x].x-p[y].x) + Abs(p[x].y-p[y].y);}

void solve(int l,int r)
{
	if(l == r) return;
	int mid = (l+r) >> 1,len = 1;
	solve(l,mid);
	for(int i = l;i <= r;++ i)
		if(p[i].ID <= mid) p[i].px = q[p[i].ID-1].x,p[i].py = q[p[i].ID-1].y;
		else p[i].px = p[i].x,p[i].py = p[i].y;
	//situation 1,2 lx<rx ly<ry;lx<rx ly>ry
	sort(p+l,p+r+1,cmpy);
	p[l].lsh = 1;
	for(int i = l+1;i <= r;++ i)
		if(p[i].py == p[i-1].py) p[i].lsh = p[i-1].lsh;
		else p[i].lsh = ++len;
	sort(p+l,p+r+1,cmpx1);
	pre.init(len); suf.init(len);
	for(int i = l;i <= r;++ i)
	{
		int ID = p[i].ID;
		if(ID <= mid)//update
		{
			pre.Add(p[i].lsh,len,g[ID]+h[ID]-s[ID]-q[ID-1].x-q[ID-1].y);
			suf.Add(len-p[i].lsh+1,len,g[ID]+h[ID]-s[ID]-q[ID-1].x+q[ID-1].y);
		}
		else//query
		{
			g[ID] = Min(g[ID],p[i].x+p[i].y+pre.Query(p[i].lsh));
			g[ID] = Min(g[ID],p[i].x-p[i].y+suf.Query(len-p[i].lsh+1));
		}
	}
	
	//situation 3,4 lx>rx ly<ry;lx>rx ly>ry
	pre.init(len); suf.init(len);
	sort(p+l,p+r+1,cmpx2);
	for(int i = l;i <= r;++ i)
	{
		int ID = p[i].ID;
		if(ID <= mid)//update
		{
			pre.Add(p[i].lsh,len,g[ID]+h[ID]-s[ID]+q[ID-1].x-q[ID-1].y);
			suf.Add(len-p[i].lsh+1,len,g[ID]+h[ID]-s[ID]+q[ID-1].x+q[ID-1].y);
		}
		else//query
		{
			g[ID] = Min(g[ID],-p[i].x+p[i].y+pre.Query(p[i].lsh));	
			g[ID] = Min(g[ID],-p[i].x-p[i].y+suf.Query(len-p[i].lsh+1));
		}
	}
	sort(p+l,p+r+1,cmpID);//You must sort it again,beceuse the following "solve" will use it.
	solve(mid+1,r);
}

signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	for(int T = Read(); T ;-- T) 
	{
		n = Read();
		for(int i = 1;i <= n;++ i) p[i].x = Read(),p[i].y = Read(),p[i].ID = i,q[i] = p[i];
		if(n <= 2) {Put(0,'\n');continue;}
		h[1] = g[1] = 0;
		for(int i = 2;i <= n;++ i) s[i] = s[i-1] + dis(i,i-1),h[i] = s[i-1],g[i] = 0;
		solve(2,n);//pay attention to the boundary
		LL ans = INF;
		for(int i = 1;i <= n;++ i) ans = Min(ans,g[i]+h[i]+s[n]-s[i]);
		Put(ans,'\n');
	}
	return 0;
}
//sorry for my poor English

后记

现在的 dp 好像都套路化了(虽然还是不会),先写出一个暴力转移方程,然后优化就过了。

dp 难在怎么优化而不是写出方程!

当然如果连暴力转移都写不出来,那没救了。

posted @ 2021-02-23 11:54  皮皮刘  阅读(74)  评论(0编辑  收藏  举报