• P5044 [IOI2018] meetings 会议


    题目链接

    写完了。感谢 ywy 学长的讲解!

    显然选最大值不优,那么我们可以根据最大值将区间分成两半,分别考虑地点在左右两边的情况,取个最小值即可。用式子来表示就是:

    [f(l,r)=min(sum_{i=l}^th_t+f(t+1,r),sum_{i=t}^rh_t+f(l,t-1)) ]

    我们考虑在最值那里计算答案。但是左右端点都不固定不好做,我们可以根据上面那个式子把询问拆成两部分,我们只考虑求右子树内部分的 (f),左子树的 (f) 可以通过翻转得到。这一步操作的作用是让区间在笛卡尔树上的部分的左边是完整的。同时方便计算答案。

    我们现在要维护 (f_{p,r}),表示以当前子树根节点 (p) 为根,从最左边到 (r) 的答案。转移直接用上面的那个式子。不过那个 (min) 很不好搞,考虑怎么把它去掉。发现随着 (r) 的增大,右边部分的增量一直是 (h_t),而左边部分的增量不会超过 (h_t),因此按照这种趋势右边会慢慢超过左边。因此,直接二分即可。

    去掉 (min) 以后就很好搞了,发现左边就是在原数组上区间加,右边就是区间赋值成等差数列。用线段树合并做即可。然后又发现实际上左右子树的 DP 数组是不交的,因此直接在一个线段树上搞即可。

    不过这样是 (O(nlog^2n)) 的,线段树上二分则是 (O(nlogn))。比较考察对线段树掌握的熟练程度。

    线段树维护区间加,区间赋值为等差数列,单点查询,线段树上二分:

    namespace SGT {
    	int ls[NN], rs[NN], root, ttot, len[NN];
    	ll val[NN], taga[NN], tagk[NN], tagp[NN];//val : right point's value
    	inline void clear() {
    		memset(val, 0, sizeof(val));
    		memset(tagp, 0, sizeof(tagp));
    		for (register int i = 1; i <= ttot; ++i)	taga[i] = tagk[i] = Rand;
    	}
    	inline void pushup(int cur) {
    		val[cur] = val[rs[cur]];
    	}
    	inline void pushtag(int cur, ll k, ll b) {
    		if (!cur)	return ;
    		tagp[cur] = 0;
    		if (len[cur] == 1)	return val[cur] = k + b, void();
    		taga[cur] = b; tagk[cur] = k;
    		val[cur] = k * len[cur] + b;
    	}
    	inline void pushplus(int cur, ll x) {
    		if (!cur)	return ;
    		val[cur] += x;
    		if (taga[cur] != Rand)	return taga[cur] += x, void();//Attention!!
    		tagp[cur] += x;
    	}
    	inline void pushdown(int cur) {
    		if (taga[cur] != Rand || tagk[cur] != Rand) {
    			pushtag(ls[cur], tagk[cur], taga[cur]);
    			pushtag(rs[cur], tagk[cur], taga[cur] + tagk[cur] * len[ls[cur]]);
    			taga[cur] = tagk[cur] = Rand;
    		}
    		if (tagp[cur])	pushplus(ls[cur], tagp[cur]), pushplus(rs[cur], tagp[cur]), tagp[cur] = 0;
    	}
    	void build(int L, int R, int &cur) {
    		cur = ++ttot;
    		ls[cur] = rs[cur] = 0;
    		len[cur] = R - L + 1; val[cur] = 0; taga[cur] = tagk[cur] = Rand;
    		if (L == R)	return ;
    		int mid = (L + R) >> 1;
    		build(L, mid, ls[cur]), build(mid + 1, R, rs[cur]);
    	}
    	void modify(int L, int R, int l, int r, ll k, ll b, int cur) {
    		if (l <= L && R <= r) { pushtag(cur, k, b); return ; }
    		pushdown(cur);
    		int mid = (L + R) >> 1;
    		if (l <= mid && r > mid) {//Attention!!!!!!!!!!
    			modify(L, mid, l, r, k, b, ls[cur]);
    			modify(mid + 1, R, l, r, k, b + 1ll * (mid - max(l, L) + 1) * k, rs[cur]);
    		} else {
    			if (l <= mid)	modify(L, mid, l, r, k, b, ls[cur]);
    			if (r > mid)	modify(mid + 1, R, l, r, k, b, rs[cur]);//Attention!!!!!!!!!
    		}
    		pushup(cur);
    	}
    	inline void Modify(int l, int r, ll k, ll b) { if (l <= r) modify(1, n, l, r, k, b, root); }
    	inline void add(int L, int R, int l, int r, ll x, int cur) {
    		if (l <= L && R <= r) { pushplus(cur, x); return ;}
    		pushdown(cur);
    		int mid = (L + R) >> 1;
    		if (l <= mid)	add(L, mid, l, r, x, ls[cur]);
    		if (r > mid)	add(mid + 1, R, l, r, x, rs[cur]);
    		pushup(cur);
    	}
    	inline void Add(int l, int r, ll x) { if (l <= r)	add(1, n, l, r, x, root); }
    	ll query(int L, int R, int pos, int cur) {
    		if (L == R)	return val[cur];
    		pushdown(cur);
    		int mid = (L + R) >> 1;
    		if (pos <= mid)	return query(L, mid, pos, ls[cur]);
    		return query(mid + 1, R, pos, rs[cur]);
    	}
    	inline ll Query(int p) { return query(1, n, p, root); }
    	int dfs(int L, int R, int l, int r, ll k, ll b, int cur) {//线段树上二分
    		if (len[cur] == 1)	return val[cur] >= k + b ? L : L - 1;
    		pushdown(cur);
    		int mid = (L + R) >> 1;
    		if (l <= L && R <= r) {//Attention!!!
    			if (val[ls[cur]] >= k * len[ls[cur]] + b)
    				return dfs(mid + 1, R, l, r, k, k * len[ls[cur]] + b, rs[cur]);
    			return dfs(L, mid, l, r, k, b, ls[cur]);
    		}
    		if (l <= mid && r > mid) {//Attention!!!!!
    			if (val[ls[cur]] >= k * (mid - max(l,L) + 1) + b)//Attention!!!!!
    				return dfs(mid + 1, R, l, r, k, k * (mid - max(l, L) + 1) + b, rs[cur]);
    			return dfs(L, mid, l, r, k, b, ls[cur]);
    		}
    		if (l <= mid)	return dfs(L, mid, l, r, k, b, ls[cur]);
    		return dfs(mid + 1, R, l, r, k, b, rs[cur]);
    	}
    	inline int Dfs(int l, int r, ll k, ll b) { return dfs(1, n, l, r, k, b, root); }
    }
    

    笛卡尔树的构建及笛卡尔树上 DP 回答询问:

    struct Queries {
    	int l, r, t;
    	ll res, ans;
    	Queries() { l = r = t = 0; res = 0; ans = inf; }//Attention!!!
    	inline void calc() {
    		ans = min(ans, res + 1ll * h[t] * (t - l + 1));
    	}
    	inline void cg() {
    		l = n - l + 1, r = n - r + 1;
    		swap(l, r);
    		res = 0;
    	}
    }qs[N];
    vector<int> vec[N];
    
    int ls[N], rs[N], stk[N], stop, Rt, tl[N], tr[N];
    int dep[N], siz[N], fa[N], son[N];
    inline void Clear()
    void dfs_son(int cur, int faa)
    int top[N];
    void dfs_chain(int cur, int topp)
    inline int get_lca(int x, int y)
    
    inline void init() {
    	for (register int i = 1; i <= n; ++i) {
    		while (stop && h[i] > h[stk[stop]])	ls[i] = stk[stop], --stop;
    		if (stop)	rs[stk[stop]] = i;
    		stk[++stop] = i;
    	}
    	Rt = stk[1];
    	dfs_son(Rt, 0);
    	dfs_chain(Rt, Rt);
    }
    
    void dfs(int cur) {
    	if (!cur)	return ;
    	dfs(ls[cur]); dfs(rs[cur]);
    	if (!ls[cur] && !rs[cur]) {
    		SGT::Modify(cur, cur, 0, h[cur]);
    	} else if (!ls[cur]) {
    		SGT::Add(cur, tr[cur], h[cur]);
    	} else if (!rs[cur]) {
    		SGT::Modify(cur, cur, 0, SGT::Query(cur - 1) + h[cur]);//Attention!!!!!!
    	} else {
    		ll tmp = SGT::Query(cur - 1);
    		int pos = SGT::Dfs(cur + 1, tr[cur], h[cur], tmp - 1ll * h[cur] * (cur - tl[cur]));
    		SGT::Modify(cur, pos, h[cur], tmp);
    		SGT::Add(pos + 1, tr[cur], 1ll * (cur - tl[cur] + 1) * h[cur]);//Attention!!!!
    	}
    	for (register unsigned int i = 0; i < vec[cur].size(); ++i) {//Attention!!!!!
    		int nw = vec[cur][i];
    		int r = qs[nw].r;
    		qs[nw].res = SGT::Query(r);
    	}
    	vec[cur].clear();
    }
    
  • 相关阅读:
    打印杨辉三角形
    Java中的基本数据类型
    C语言网上点餐系统1.0
    C语言中常用的输入和输出函数
    ubantu忘记登录密码怎么办?(ubantu16.04)
    Linux常用服务器构建-samba(ubantu)
    Vue为文件目录设置别名
    css内容占满全屏
    Vue中引入了better-scroll后 页面上的点击事件不生效了
    javascript中获取dom元素高度和宽度的方法
  • 原文地址:https://www.cnblogs.com/JiaZP/p/13576862.html
Copyright © 2020-2023  润新知