[APIO2016] 烟火表演 题解

[APIO2016] 烟火表演 题解


知识点

可并堆(左偏树),Slope Trick,优化 DP。


分析

\(7\%\)

初一数学绝对值问题,中位数处理即可……

\(26\%\)

注意到距离范围极小,那么可以 DP:设 \(f_{u,i}\) 为到点 \(u\) 时,后面的烟花引爆时间为 \(v\) 的最小代价。

\[f_{u,i} = \sum_{v \in Son_u}{\min_{j = 0}^i{(|j + l - i| + f_{v,j})}} \\ \]

直接做是 \(O(n|\Sigma|^2)\) 的,可以拿到分数。

\(55\%\)

你有能力写这个不如直接上正解……

\(100\%\)

首先,对于每个叶子结点,DP 数组上传父节点时就相当于一个常数函数加一次绝对值函数,这是一个下凸包,那么根据闵可夫斯基和的性质,结合数学归纳法,可知之后每一个 DP 数组都相当于一个下凸函数 \(f_u{(x)}\),并且所有斜率都是整数,并且级别在 \(O(n)\),可以使用 Slope Trick。

设现在有一个函数 \(f_u(x)\) 向父节点转移,边的长度为 \(w\),生成的新函数为 \(f_u'(x)\)

对于一个 \(f_u(x)\),由于斜率全都是整数,那么肯定会在某个区间 \([L,R]\) 或者单个点 \(\{ X \}\) 有一段连续的最小值(单个点在这里也当做 \(L=R\) 的区间看待),那么可以得到:

\[f_u'(x) = \begin{cases} f_u(x) + w & x \in [0,L) \\ {f_u(x)}_{\min} + (L + w) - x & x \in [L,L+w] \\ {f_u(x)}_{\min} & x \in (L+w,R+w] \\ {f_u(x)}_{\min} + x - (L + w) & x \in (R+w,+\infin) \\ \end{cases} \]

这个式子其实就代表我们把 \(f_u(x)\) 的最小值区间 \([L,R]\) 向右推 \(w\) 个单位长度形成了 \(f_u'(x)\) 的最小值区间 \([L+w,R+w]\)。然后我们就可以把 \(f_u'(x)\) 和父节点的 \(f_{fa_u}(x)\) 合并。

那么现在来考虑如何找出一个 \(f_u(x)\)\([L,R]\) 区间:我们看到从子节点传上来的 \(f_{v}'(x),v \in Son_u\),它在 \([L,+\infin)\) 部分形成三段斜率分别为 \(-1,0,1\) 的线段和射线,设 \(u\)\(cnt\) 个子节点,那么 \(f_u(x)\) 的最后一段的斜率一定是 \(cnt\),也就是每个子节点的斜率为 \(1\) 的那一段叠加而成,那么我们可以把子节点的 \([L+w,R+w]\) 排序后从右边将结点一个个减掉,重复 \(cnt-1\) 次,就可以找到自己的 \([L,R]\) 了。

那么我们可以用大根可并堆实现,选用配对堆或左偏树皆可:

  • \(f_u(x) \to f_u'(x)\)

    \(u\) 的左偏树中 \([L,R]\) 两结点删除,加入 \([L+w,R+w]\)

  • \(f_u'(u) \to f_{fa_u}(x)\)

    直接把 \(u\) 的左偏树合并到 \(fa_u\) 的左偏树上。

  • \(f_u(x)\) 中找到 \([L,R]\)

    弹出堆顶 \(cnt-1\) 次,剩下堆的堆顶两个即为 \(L,R\)

最后再来考虑统计答案:首先 \(f_1(0)\)\(\sum_{i=2}^{n+m} C_{i}\),然后我们又有由斜率为 \(-1\) 叠加成的的函数的所有转折点,那么就可以很简单的求出了。

代码

//#define Plus_Cat ""
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define RCL(a,b,c,d) memset(a,b,sizeof(c)*(d))
#define tomin(a,...) ((a)=min({(a),__VA_ARGS__}))
#define tomax(a,...) ((a)=max({(a),__VA_ARGS__}))
#define FOR(i,a,b) for(int i(a); i<=(int)(b); ++i)
#define DOR(i,a,b) for(int i(a); i>=(int)(b); --i)
#define EDGE(g,i,x,y) for(int i(g.h[x]),y(g[i].v); ~i; y=g[i=g[i].nxt].v)
using namespace std;
constexpr int N(3e5+10);

namespace IOEcat {
#define isD(c) ('0'<=(c)&&(c)<='9')
#define DE(...) E(#__VA_ARGS__,__VA_ARGS__)
	struct Icat {

		char getc() {
			return getchar();
		}

		template<class T>void operator ()(T &x) {
			static bool sign(0);
			static char ch(0);
			sign=0,x=0;
			while(ch=getc(),!isD(ch))if(ch=='-')sign=1;
			do x=(x<<1)+(x<<3)+(ch^48);
			while(ch=getc(),isD(ch));
			if(sign)x=-x;
		}

		template<class T,class...Types>void operator ()(T &x,Types&...args) {
			return (*this)(x),(*this)(args...);
		}

	} I;
	struct Ocat {

		void putc(char c) {
			putchar(c);
		}

		template<class T>void operator ()(T x,const char lst='\n') {
			static int top(0);
			static char st[100];
			if(x<0)x=-x,putc('-');
			do st[++top]=(x%10)^48,x/=10;
			while(x);
			while(top)putc(st[top--]);
			putc(lst);
		}

		template<class T,class...Types>void operator ()(const T x,const char lst='\n',const Types...args) {
			return (*this)(x,lst),(*this)(args...);
		}

	} O;
	struct Ecat {

		template<class T>void operator ()(const char *fmt,const T x) {
			cerr<<fmt<<':'<<x<<'.'<<endl;
		}

		template<class T,class...Types>void operator ()(const char *fmt,const T x,const Types...args) {
			while(*fmt^',')cerr<<*fmt++;
			return cerr<<':'<<x<<" ,",(*this)(++fmt,args...);
		}

	} E;

} using namespace IOEcat;

int n,m;
int fa[N],pa[N];
ll ans;
vector<int> g[N];

namespace Subtask1 { /*中位数*/ /*7%*/
	
	bool Check() {
		return n==1;
	}
	
	int Cmain() {
		sort(pa+2,pa+n+1);
		FOR(i,2,n)ans+=abs(pa[i]-pa[(n+2)>>1]);
		O(ans,'\n');
		return 0;
	}
	
}

namespace Subtask2 { /*O(n^3) DP*/ /*26%*/
	constexpr int N(3e2+10);
	int f[N][N];
	
	bool max_dis(int u,ll dis) {
		if((dis+=pa[u])>300)return 0;
		for(int v:g[u])if(!max_dis(v,dis))return 0;
		return 1;
	}
	
	bool Check() {
		return n<=300&&max_dis(1,0);
	}
	
	void dp(int u) {
		if(u>n)return RCL(f[u],INF,f[u],1),f[u][0]=0,void();
		for(int v:g[u]) {
			dp(v);
			FOR(i,0,N-5) {
				int mi(INF);
				FOR(j,0,i)tomin(mi,abs(j+pa[v]-i)+f[v][j]);
				f[u][i]+=mi;
			}
		}
	}
	
	int Cmain() {
		dp(1),ans=INF;
		FOR(i,0,N-5)tomin(ans,(ll)f[1][i]);
		O(ans,'\n');
		return 0;
	}
	
}

namespace Subtask { /*斜堆维护函数转折点*/ /*100%*/
	struct Self_Adjusting_Heap {
		int tot;
		int rt[N];
		struct node {
			int ls,rs;
			ll val;
		} tr[N<<1];
		
		int &operator ()(int i) {
			return rt[i];
		}
		
		node &operator [](int i) {
			return tr[i];
		}
		
		int New(ll val) {
			return tr[++tot]={0,0,val},tot;
		}
		
		int Merge(int p,int q) {
			if(!p||!q)return p|q;
			if(tr[p].val<tr[q].val)swap(p,q);
			return tr[p].rs=Merge(tr[p].rs,q),swap(tr[p].ls,tr[p].rs),p;
		}
		
		void Pop(int &p) {
			p=Merge(tr[p].ls,tr[p].rs);
		}
		
	} hp;
	
	int Cmain() {
		DOR(u,n,2) {
			ans+=pa[u];
			FOR(i,2,g[u].size())hp.Pop(hp(u));
			ll R(hp[hp(u)].val);
			hp.Pop(hp(u));
			ll L(hp[hp(u)].val);
			hp.Pop(hp(u));
			hp(u)=hp.Merge(hp(u),hp.New(R+pa[u]));
			hp(u)=hp.Merge(hp(u),hp.New(L+pa[u]));
			hp(fa[u])=hp.Merge(hp(fa[u]),hp(u));
		}
		FOR(i,1,g[1].size())hp.Pop(hp(1));
		while(hp(1))ans-=hp[hp(1)].val,hp.Pop(hp(1));
		O(ans,'\n');
		return 0;
	}
	
}

int main() {
#ifdef Plus_Cat
	freopen(Plus_Cat ".in","r",stdin),freopen(Plus_Cat ".out","w",stdout);
#endif
	I(n,m),n+=m;
	FOR(i,2,n)I(fa[i],pa[i]),g[fa[i]].push_back(i);
	if(Subtask1::Check())return Subtask1::Cmain();
	if(Subtask2::Check())return Subtask2::Cmain();
	return Subtask::Cmain();
}
posted @ 2025-01-06 16:29  Add_Catalyst  阅读(17)  评论(0)    收藏  举报