JZOJ6232 【NOI2019模拟2019.6.25】喜欢最最痛(凸函数,贪心,动态dp)

Description:


神树大人种了一棵有边权的树,由于这是神树大人种的树,所以这棵树被命名为神
神树。神神树的边权为正. 整. 数. 。神树大人命令龚诗锋从 1 号点开始走一个路径并最终
回到1 号点,且这条路径经过了所有的边。一条路径的代价就是它经过的边的边权之
和。龚诗锋可以加若干条额外边,第 i 条加的额外边的边权为正. 整. 数. Ai。注. 意. ,龚. 诗. 锋.
不. 一. 定. 要. 经. 过. 所. 有. 的. 额. 外. 边. 。
由于龚诗锋喜欢最最痛,所以对于所有的K ∈[0;m],你需要输出允许加<= K 条额
外边的最小路径代价。
1<=n<=1e5,5s

题解:


不难想到假设求出了 f [ i ] f[i] f[i]表示 i i i条不相交路径的最大长度和,那么对一个K,就需要求出l,使 2 ∗ 边 权 和 − f [ i ] + 前 K 条 边 中 前 l 小 的 和 2*边权和-f[i]+前K条边中前l小的和 2f[i]+Kl的最小值

当然这个可以暴力用个平衡树(离线线段树)二分。

注意到 f f f是个凸函数,然后前l小的和也是个凸函数,且随着K的增大,l只会右移,所以可以用两个multiset维护,一个存选了的l条边,另一个存没选的,细节比较简单不讲了。

那么问题在于求f,可以暴力对拍,复杂度 O ( n 2 ) O(n^2) O(n2)

然后这个东西是优化不了的,考虑一个贪心,每次选直径,把直径边权取反,继续找直径。

显然我是不会证明这个东西的正确性的,但是至少能找到i条不相交的路径。

那么这个东西显然可以用ddp优化,update细节有点多,还需要用一个multiset维护一个点的虚儿子的最小和次小,access要改改写法。

重载一堆东西以后还是可以写的hhh

复杂度 O ( n   l o g 2 n ) O(n~log^2n) O(n log2n),常数巨大

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i <  B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

int by;

const int N = 2e5 + 5;

int n, m, x, y, z, w[N];
int v[N];

struct P {
	int x, y; ll z;
	P(){}
	P(int _x, int _y, ll _z) { x = _x, y = _y, z = _z;}
};
bool operator <(P a, P b) {
	if(a.z < b.z) return 1;
	if(a.z > b.z) return 0;
	if(a.x < b.x) return 1;
	if(a.x > b.x) return 0;
	return a.y < b.y;
}
P operator +(P a, P b) { return P(!a.x ? b.x : a.x, !b.y ? a.y : b.y, a.z + b.z);}

P fan(P a) { swap(a.x, a.y); return a;}

struct nod {
	P f1, f2, g, v;
};

nod a[N][2][2];
multiset<P> s[N], g[N];
int fa[N], t[N][2], rev[N], rf[N], dd[N], pf[N];
#define i0 t[i][0]
#define i1 t[i][1]

P calc1(multiset<P> &s) {
	if(!s.size()) return P(0, 0, 0);
	return *s.rbegin();
}
P calc2(multiset<P> &s) {
	if(s.size() < 2) return P(0, 0, 0);
	return (*++s.rbegin());
}
template<class T> void cmax(T &a, T b) {if(a < b) a = b;} 
void upd(int i, multiset<P> &s, multiset<P> &g, int V, nod &a, nod b, nod c) {
	P w = P(i, i, V);
	a.f1 = b.f1; cmax(a.f1, b.v + w + c.f1); cmax(a.f1, b.v + w + calc1(s));
	a.f2 = c.f2; cmax(a.f2, b.f2 + w + c.v); cmax(a.f2, fan(calc1(s)) + w + c.v);
	a.g = max(b.g, c.g);
	if(g.size()) cmax(a.g, *g.rbegin());
	cmax(a.g, b.f2 + w + c.f1);
	cmax(a.g, fan(calc1(s)) + w + calc2(s));
	cmax(a.g, b.f2 + w + calc1(s));
	cmax(a.g, fan(calc1(s)) + w + c.f1);
	a.v = b.v + w + c.v;
}
void upd(int i) {
	upd(i, s[i], g[i], v[i], a[i][0][0], a[i0][0][0], a[i1][0][0]);
	upd(i, s[i], g[i], v[i], a[i][1][0], a[i1][1][0], a[i0][1][0]);
	upd(i, s[i], g[i], -v[i], a[i][0][1], a[i0][0][1], a[i1][0][1]);
	upd(i, s[i], g[i], -v[i], a[i][1][1], a[i1][1][1], a[i0][1][1]);
}
void fan(int i) {
	if(i) {
		swap(i0, i1);
		swap(a[i][0][0], a[i][1][0]);
		swap(a[i][0][1], a[i][1][1]);
		rev[i] ^= 1;
	}
}
void fu(int i) {
	if(i) {
		v[i] = -v[i];
		swap(a[i][0][0], a[i][0][1]);
		swap(a[i][1][0], a[i][1][1]);
		rf[i] ^= 1;
	}
}
void down(int i) {
	if(rev[i]) fan(i0), fan(i1), rev[i] = 0;
	if(rf[i]) fu(i0), fu(i1), rf[i] = 0;
}
void xc(int x) {
	for(; x; x = fa[x]) dd[++ dd[0]] = x;
	while(dd[0]) down(dd[dd[0] --]);
}
int lr(int x) { return t[fa[x]][1] == x;}
void rotate(int x) {
	int y = fa[x], k = lr(x);
	t[y][k] = t[x][!k]; if(t[x][!k]) fa[t[x][!k]] = y;
	fa[x] = fa[y]; if(fa[y]) t[fa[y]][lr(y)] = x;
	fa[y] = x; t[x][!k] = y; pf[x] = pf[y];
	upd(y); upd(x);
}
void splay(int x, int y) {
	xc(x);
	for(; fa[x] != y; rotate(x))
		if(fa[fa[x]] != y) rotate(lr(x) == lr(fa[x]) ? fa[x] : x);
}
void access(int x) {
	int xx = x;
	for(int y = 0; x; ) {
		splay(x, 0);
		if(y) {
			s[x].erase(s[x].find(a[y][0][0].f1));
			g[x].erase(g[x].find(a[y][0][0].g));
		}
		y = x, x = pf[x];
	}
	x = xx;
	for(int y = 0; x; ) {
		splay(x, 0); fa[t[x][1]] = 0, pf[t[x][1]] = x;
		if(t[x][1]) {
			s[x].insert(a[t[x][1]][0][0].f1);
			g[x].insert(a[t[x][1]][0][0].g);
		}
		t[x][1] = y, fa[y] = x, pf[y] = 0;
		y = x, upd(x), x = pf[x];
	}
}
void mr(int x) {
	access(x); splay(x, 0);
	fan(x);
}
void link(int x, int y) {
	mr(x); mr(y);
	pf[x] = y;
	s[y].insert(a[x][0][0].f1);
	g[y].insert(a[x][0][0].g);
	access(x);
}

ll f[N];

ll sum, ans;
multiset<int> s1, s2;

int main() {
	freopen("love.in", "r", stdin);
	freopen("love.out", "w", stdout);
	scanf("%d %d", &n, &m);
	fo(i, 1, n - 1) {
		scanf("%d %d %d", &x, &y, &z);
		ans += z * 2;
		v[n + i] = z;
		link(x, n + i); link(n + i, y);
	}
	fo(i, 1, n) {
		mr(1);
		P p = a[1][0][0].g;
		f[i] = f[i - 1];
		if(p.z >= 0) {
			f[i] += p.z;
			mr(p.x); access(p.y); splay(p.y, 0);
			fu(p.y);
		}
	}
	pp("%lld ", ans);
	int l = 0;
	fo(i, 1, m) {
		scanf("%d", &w[i]);
		if(s1.size() && (*s1.rbegin()) > w[i]) {
			sum += w[i] - (*s1.rbegin());
			s2.insert(*s1.rbegin());
			s1.erase(s1.find(*s1.rbegin()));
			s1.insert(w[i]);
		} else s2.insert(w[i]);
		while(s2.size() && (sum + *s2.begin()) - f[l + 1] <= sum - f[l]) {
			sum += *s2.begin();
			s1.insert(*s2.begin());
			s2.erase(s2.begin());
			l ++;
		}
		pp("%lld ", ans + sum - f[l]);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
信息学奥赛是一项重要的竞赛活动,旨在培养学生的计算机科学和信息技术能力。省选是指在全省范围内举办的预赛,参赛选手需要通过省选才能晋级到更高层次的比赛。在省选中发挥出色的选手将有机会代表本省参加全国信息学奥林匹克竞赛(NOI)。 而noi_pdf-2020.12.29.rar 是一个压缩文件的命名,其中包含了一份关于NOI的PDF文档。这个文件可能包含了有关NOI的相关资料,例如竞赛规则、题目类型、考试要求等等。通过研究这个文件,选手可以更好地准备信息学奥赛,提高竞赛成绩。 对于想要参加信息学奥赛的同学们来说,可以利用这份PDF文档来深入了解NOI的要求和考试内容。首先,可以仔细阅读竞赛规则,了解比赛的时间、地点、参赛资格等重要信息。其次,可以通过研究题目类型和考试要求,明确自己需要学习和复习的内容,制定合理的备考计划。此外,可以通过查阅往年的竞赛题目和解答,进行练习和模拟考试,提高解题能力和应变能力。 综上所述,信息学奥赛省选和noi_pdf-2020.12.29.rar对于想要参加信息学奥赛的同学们来说都有重要的意义。省选是选拔出优秀选手的一个重要阶段,而noi_pdf-2020.12.29.rar则提供了有关NOI的重要资料,帮助选手更好地准备竞赛。希望通过努力学习和准备,同学们可以在比赛中取得优异成绩,提升自己的计算机科学和信息技术能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值