凸性 DP 优化
首先这里点名
闵可夫斯基和
这个东西时是用来优化一类凸函数卷积的,一般就是背包或者分治时使用。最常用的是
首先考虑这个卷积式:
其中
性质
考虑卷积的组合意义,假设我们将
性质
首先把
因此,我们有了第二个性质后,不难通过归并排序直接直接做到
P9962 THUPC 2024 初赛 一棵树
首先考虑一些
细节很多,调了两百年。
qwq
#include<bits/stdc++.h> #define ll long long using namespace std; mt19937 rnd(114514); const int N = 5e5 + 10; int n; ll k; struct edge{ int v, next; }edges[N << 1]; int head[N], idx; void add_edge(int u, int v){ edges[++idx] = {v, head[u]}; head[u] = idx; } struct node{ int ls, rs, siz; int key; ll tag, sum, val; }tn[N * 2]; int tot, rub[N], rt[N], siz[N]; int newnode(ll val){ int id = ((rub[0]) ? (rub[rub[0]--]) : (++tot)); tn[id] = (node){0, 0, 1, (int)rnd(), 0, val, val}; return id; } struct FHQ_treap{ void pushup(int o){ tn[o].sum = tn[tn[o].ls].sum + tn[tn[o].rs].sum + tn[o].val; tn[o].siz = tn[tn[o].ls].siz + tn[tn[o].rs].siz + 1; } void addtag(int o, ll v){tn[o].sum += 1ll * v * tn[o].siz; tn[o].tag += v; tn[o].val += v;} void pushdown(int o){ if(!tn[o].tag) return; addtag(tn[o].ls, tn[o].tag); addtag(tn[o].rs, tn[o].tag); tn[o].tag = 0; } void split_val(int o, int val, int& x, int& y){ if(!o){x = y = 0; return;} pushdown(o); if(tn[o].val <= val){ x = o; split_val(tn[o].rs, val, tn[o].rs, y); } else { y = o; split_val(tn[o].ls, val, x, tn[o].ls); } pushup(o); } void split_siz(int o, int val, int& x, int& y){ if(!o){x = y = 0; return;} pushdown(o); if(tn[tn[o].ls].siz + 1 <= val){ x = o; val -= tn[tn[o].ls].siz + 1; split_siz(tn[o].rs, val, tn[o].rs, y); } else{ y = o; split_siz(tn[o].ls, val, x, tn[o].ls); } pushup(o); } int merge(int x, int y){ if(!x || !y) return x + y; pushdown(x); pushdown(y); if(tn[x].key > tn[y].key){ tn[x].rs = merge(tn[x].rs, y); pushup(x); return x; } else { tn[y].ls = merge(x, tn[y].ls); pushup(y); return y; } } void ins(int& o, int val){ if(!o){o = newnode(val); return;} int x, y; split_val(o, val, x, y); o = merge(merge(x, newnode(val)), y); return; } void clr(int& o){ if(!o) return; pushdown(o); rub[++rub[0]] = o; clr(tn[o].ls); clr(tn[o].rs); o = 0; } void Segclr(int& o, int l){ int x, y; split_siz(o, l, x, y); clr(y); o = x; } void Segadd(int o, int l, int r, ll v){ //cout << o << " " << l << " " << r << " " << v << "\n"; if(l > r || l > tn[o].siz) return; int x, y, z, q; split_siz(o, l - 1, x, y); split_siz(y, r, z, q); addtag(z, v); o = merge(merge(x, z), q); } void db(int o){ if(!o) return; pushdown(o); db(tn[o].ls); cout << tn[o].val << " "; db(tn[o].rs); } }tr; void dfs(int u, int fa){ int son = 0; siz[u] = 1; for(int i = head[u]; i; i = edges[i].next){ int v = edges[i].v; if(v == fa) continue; dfs(v, u); int l = k / 2; siz[u] += siz[v]; if(siz[v] > siz[son]) son = v; if(k & 1) tr.Segadd(rt[v], 1, min(siz[v], l), -2), tr.Segadd(rt[v], l + 2, siz[v], 2); else tr.Segadd(rt[v], 1, min(l, siz[v]), -2), tr.Segadd(rt[v], l + 1, siz[v], 2); //cout << v << " 2:" << "\n"; tr.db(rt[v]); cout << "\n"; } rt[u] = rt[son]; tr.ins(rt[u], 0); tr.Segclr(rt[u], k); // cout << u << " 1:" << "\n"; // tr.db(rt[u]); cout << "\n"; for(int i = head[u]; i; i = edges[i].next){ int v = edges[i].v; if(v == fa || v == son) continue; int now = rub[0]; tr.clr(rt[v]); for(int j = rub[0]; j > now; j--) tr.ins(rt[u], tn[rub[j]].val); tr.Segclr(rt[u], k); } tr.Segclr(rt[u], k); // cout << u << " 1:" << "\n"; // tr.db(rt[u]); cout << "\n"; } signed main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n >> k; for(int i = 1; i < n; i++){ int x, y; cin >> x >> y; add_edge(x, y); add_edge(y, x); } dfs(1, 0); cout << tn[rt[1]].sum + 1ll * k * (n - 1); return 0; }
本文作者:Little_corn
本文链接:https://www.cnblogs.com/little-corn/p/18684410
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步