社区讨论

蒟蒻刚学OI,求助大佬

P3642[APIO2016] 烟花表演参与者 27已保存回复 28

讨论操作

快速查看讨论及其快照的属性,并进行相关操作。

当前回复
28 条
当前快照
1 份
快照标识符
@mi6xw4f3
此快照首次捕获于
2025/11/20 12:36
4 个月前
此快照最后确认于
2025/11/20 16:23
4 个月前
查看原帖
WA 40 不知道为什么。
思路和题解有所不同,但都是DP。
考虑 fu(x)f_u(x) 表示节点 uu 子树中叶子深度改为 xx 的最小代价。
是一个连续的下凸函数,且由斜率为整数的一次函数拼接而成。
考虑 fu(x)f_u(x)fvf_v 转移,这里 vvuu 的儿子。
fu(x)=v[  miny(fv(y)+xywv)  ]f_u(x)=\sum_v[\;\min_y(f_v(y)+|x-y-w_v|)\;],这里 wvw_v 表示 uuvv 的导火索长度。
可以发现其中 miny(fv(y)+xywv)\min_y(f_v(y)+|x-y-w_v|) 的部分关于自变量 xx 是一个形如:
fv(x)={C+(Lx),x<LC,LxRC+(xR),x>R{f_v}'(x)=\left\{\begin{matrix}C+(L-x)&,&x<L\\C&,&L\le x\le R\\C+(x-R)&,&x>R\end{matrix}\right.
的函数。
其中 C,L,RC,L,R 只与 fvf_v 中斜率为 00 的一段有关。
故对于任意的 fuf_u,只需记录它的斜率为 00 的一段的端点和函数值即可。
也就是结构体 Dat 中的 x, y, z 三个变量。
fu(x)=vfv(x)f_u(x)=\sum_v{f_v}'(x) 中,需要将若干个形如上述的函数合并,并求出斜率为 00 的一段。请参见代码。
CPP
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define INF 0x3f3f3f3f3f3f3f3f
int N,M;
int h[600005],nxt[600005],to[600005],tot; LL w[600005];
void ins(int x,int y,LL z){nxt[++tot]=h[x];to[tot]=y;w[tot]=z;h[x]=tot;}
struct Dat{
	LL x,y,z;
	Dat(LL x=0,LL y=0,LL z=0):x(x),y(y),z(z){}
}f[600005],st[600005];
Dat operator+(Dat p1,LL p2){
	return Dat(p1.x+p2,p1.y+p2,p1.z);
}
LL v[600005]; int t;
int main(){
	scanf("%d%d",&N,&M);
	for(int i=2;i<=N+M;++i){
		int x; LL y;
		scanf("%d%lld",&x,&y);
		ins(x,i,y);
	}
	for(int i=N;i>=1;--i){
		t=0;
		for(int j=h[i];j;j=nxt[j]){
			st[++t]=f[to[j]]+w[j];
			v[2*t-1]=st[t].x;
			v[2*t]=st[t].y;
		}
		sort(v+1,v+t+t+1);
		f[i].x=v[t], f[i].y=v[t+1];
		for(int j=1;j<=t;++j){
			if(v[t]<st[j].x) f[i].z+=st[j].z+st[j].x-v[t];
			else if(v[t]>st[j].y) f[i].z+=st[j].z+v[t]-st[j].y;
			else f[i].z+=st[j].z;
		}
	}
	printf("%lld\n",f[1].z);
	return 0;
}

回复

28 条回复,欢迎继续交流。

正在加载回复...