AT1757 花火 题解

这是我根据官方题解复盘出来的做法。

\(O(n^2)\)DP显然。

\(dp[i][j]\)表示i时刻在位置j的最小代价。

转移:

\[ dp[i][j]=min_{k<=j}(dp[i-1][k])+ ( i 时间内在位置 j 所有烟花的不满值之和) \]

\(dp'[i][j]=min_{k<=j}(dp[i][k])\) (就是做了一个前缀min)

\(dp[i][j]=dp'[i-1][j]+\) ( \(i\) 时间内在位置 \(j\) 所有烟花的不满值之和)

\(dp'[i][j]=min(dp'[i][j-1],dp[i][j])\)

从“ \(i\) 时间内在位置 \(j\) 所有烟花的不满值之和”入手,令\(f(i)\)表示在 \(i\) 位置,某一烟花造成的不满值。我们可以发现, $f(i)=|x_i-i| $( \(x_i\) 表示烟花绽放的位置) 。

显然,\(f(i)\)是一个下凸函数,那么对于在同一时刻绽放的所有烟花,\(f(i)\)的和也为下凸函数。(若干个下凸函数的和仍然是下凸函数)

考虑我们的 \(dp\) 过程,是先加了同一时刻绽放的所有烟花的不满值的和,然后做了一遍前缀min,然后重复。

最开始dp函数是一条 \(y=0\) 的直线,加上 同一时刻绽放的所有烟花的不满值的和(一个下凸函数)之后仍是一个下凸函数,然后做一遍前缀min,仍然是一个下凸函数,而且对于下凸函数取min就是把后面一段导数大于0的一段的导数改为0。

为了方便维护下凸函数,官方题解采取了维护差分序列的方法,下面是我根据官方题解写的代码,线段树区间覆盖部分蒯的是这篇博客

#include<bits/stdc++.h>
using namespace std;
const long long maxn = 100007,INF=214748364700000;
long long Max[maxn << 2], changetag[maxn << 2], addtag[maxn << 2];
long long a[maxn],t[maxn],m,ans(INF),dp[maxn];
long long n, LL;
 
inline void PushUp(long long rt) {
	Max[rt] = max(Max[rt<<1], Max[rt<<1|1]);
}
 
inline void PushDown(long long rt) {
	if (changetag[rt] != -INF) {
		Max[rt<<1] = changetag[rt];
		Max[rt<<1|1] = changetag[rt];
		changetag[rt << 1] = changetag[rt];
		addtag[rt << 1] = 0;
		changetag[rt << 1 | 1] = changetag[rt];
		addtag[rt << 1 | 1] = 0;
		changetag[rt] = -INF;
	} else if (addtag[rt]) {
		Max[rt<<1] += addtag[rt];
		Max[rt<<1|1] += addtag[rt];
		if (changetag[rt<<1] != -INF) changetag[rt<<1] += addtag[rt];
		else addtag[rt<<1] += addtag[rt];
		if (changetag[rt<<1|1] != -INF) changetag[rt<<1|1] += addtag[rt];
		else addtag[rt<<1|1] += addtag[rt];
		addtag[rt] = 0;
	}
}
 
void build(long long rt, long long l, long long r) {
	changetag[rt] = -INF;
	addtag[rt] = 0;
	if (l == r) {
		Max[rt] = 0;
		return;
	}
	long long m = (l + r) >> 1;
	build(rt<<1, l, m);
	build(rt<<1|1, m+1, r);
//	PushUp(rt);
}
 
void change(long long rt, long long l, long long r, long long L, long long R, long long C) {
	if (L <= l && r <= R) {
		Max[rt] = C;
		changetag[rt] = C;
		addtag[rt] = 0;
		return;
	}
	long long m = (l + r) >> 1;
	PushDown(rt);
	if (L <= m) change(rt<<1, l, m, L, R, C);
	if (R > m) change(rt<<1|1, m+1, r, L, R, C);
	PushUp(rt);
}
 
void add(long long rt, long long l, long long r, long long L, long long R, long long C) {
	if (L <= l && r <= R) {
		Max[rt] = Max[rt] + C;
		if (changetag[rt] == -INF) addtag[rt] += C;
		else changetag[rt] += C;
		return;
	}
	long long m = (l + r) >> 1;
	PushDown(rt);
	if (L <= m) add(rt<<1, l, m, L, R, C);
	if (R > m) add(rt<<1|1, m+1, r, L, R, C);
	PushUp(rt);
}
 
long long query(long long rt, long long l, long long r, long long L, long long R) {
	if (L <= l && r <= R) return Max[rt];
	long long m = (l + r) >> 1, res = -INF;
	PushDown(rt);
	if (L <= m) res = max(res, query(rt<<1, l, m, L, R));
	if (R > m) res = max(res, query(rt<<1|1, m+1, r, L, R));
	return res;
}
long long read() {
	long long x(0);
	char ch(getchar());
	while(ch>'9'||ch<'0')ch=getchar();
	while(ch<='9'&&ch>='0') {
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x;
}
int main() {
//	freopen("testdata.in","r",stdin);
	n=read();
	LL=read();
	for (long long i = 1; i <= n; ++i)
		scanf("%lld%lld",&t[i],&a[i]);
	build(1, 1, LL);
	long long head(1),dps(a[1]);
	while (head<=n) {
		if(a[head])add(1,1,LL,1,a[head],-1);
		if(a[head]!=LL)add(1,1,LL,a[head]+1,LL,1);
		while(t[head]==t[head+1]) {
			++head;
			dps+=a[head];
			if(a[head])add(1,1,LL,1,a[head],-1);
			if(a[head]!=LL)add(1,1,LL,a[head]+1,LL,1);
		}
		long long l(1),r(LL),mid((l+r)>>1);
		while(l<r) {
			if(query(1,1,LL,mid,mid)<0)l=mid+1;
			else r=mid;
			mid=(l+r)>>1;
		}
//		prlong longf("%d ",r);
		if(r!=LL)
			change(1,1,LL,r,LL,0);
		else if(query(1,1,LL,LL,LL)>0)change(1,1,LL,LL,LL,0);
//		for(long long i=1; i<=LL; i++)
//			prlong longf("%d ",query(1,1,LL,i,i));
//		prlong longf("\n");
		++head;
		dps+=a[head];
	}
	dp[0]=dps;
	for(long long i=1; i<=LL; i++) {
		dp[i]=dp[i-1]+query(1,1,LL,i,i);
		ans=min(ans,dp[i]);
	}
	cout<<ans<<endl;
	return 0;
}
posted @ 2020-06-18 21:52  永无岛  阅读(151)  评论(0编辑  收藏  举报