来自学长的馈赠7
rand 2 分数255 忌骄,忌躁,这只是题目简单,不要被假象冲昏头
题纲:T1:图论,思维题
T2:板子,求最长不降序列
T3:线段树维护树上两点最值距离
T4:树上启发式合并(小的往大的里面并)+贪心策略
T3:给你一棵树,a,b,c,d,询问(ab)的点和(cd)点所组成的点对最远距离,多组询问
(1)结论:(ab)区间(A,B)是最远点对,(cd)区间(C,D)是,那么ab+cd的最远一定是(A,B)(A,D),(B,C),(B,D)中的,可以用树上某点到其他点的最远距离是直径一个端点解释,可合并区间,那就是线段树
(2)技巧:线段树传址调用,直接Query(&a,&b)一路算下a,b就直接是答案了
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 2;
const ll mod = 1e9 + 7;
const int INF = 2147483647;
const int lim = 1e4 + 1;
int n, siz[maxn], dep[maxn], fa[maxn];
int son[maxn], top[maxn], m, du[maxn];
ll ans, dis[maxn];
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch > '9' || ch < '0')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
struct node
{
int next, to, w;
}a[maxn<<1];
int head[maxn], len;
void add(int x, int y, int w)
{
a[++len].to = y; a[len].next = head[x]; a[len].w = w;
head[x] = len;
}
void find_heavy_edge(int u, int fat, int depth)
{
//printf("u = %d\n", u);
fa[u] = fat;
dep[u] = depth;
siz[u] = 1;
son[u] = 0;
int maxsize = 0;
for(int i=head[u]; i; i=a[i].next)
{
int v = a[i].to;
if(dep[v]) continue;
dis[v] = dis[u] + a[i].w;
//printf("dis[%d] = %d\n", v, dis[v]);
find_heavy_edge(v, u, depth+1);
du[u]++;
//dis[v] = dis[u] + a[i].w;
//printf("dis[%d] = %d\n", v, dis[v]);
siz[u] += siz[v];
if(siz[v] > maxsize)
{
maxsize = siz[v];
son[u] = v;
}
}
}
void connect_heavy_dege(int u, int ancestor)
{
//dfn[u] = ++ntime;
top[u] = ancestor;
//rnk[ntime] = u;
if(son[u])
{
connect_heavy_dege(son[u], ancestor);
}
for(int i=head[u]; i; i=a[i].next)
{
int v = a[i].to;
if(v == son[u] || v == fa[u]) continue;
connect_heavy_dege(v, v);
}
}
int LCA(int x, int y)
{
while(top[x] != top[y])
{
if(dep[top[x]] < dep[top[y]]) swap(x, y);
x = fa[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
return x;
}
ll get_dis(int x, int y)
{
int lca = LCA(x, y);
ll ans = dis[x]+dis[y]-2*dis[lca];
return ans;
}
#define lson (rt<<1)
#define rson (rt<<1|1)
struct node2
{
int l, r, L, R;
}tree[maxn<<2];
void IWantToUseALongNameOnlyForHappiness(int a1, int a2, int b1, int b2, int &c1, int &c2)
{
ll ans = 0;
ll a = get_dis(a1, a2);
ll b = get_dis(a1, b1);
ll c = get_dis(a1, b2);
ll d = get_dis(a2, b1);
ll e = get_dis(a2, b2);
ll f = get_dis(b1, b2);
if(a > ans) ans = a, c1 = a1, c2 = a2;
if(b > ans) ans = b, c1 = a1, c2 = b1;
if(c > ans) ans = c, c1 = a1, c2 = b2;
if(d > ans) ans = d, c1 = a2, c2 = b1;
if(e > ans) ans = e, c1 = a2, c2 = b2;
if(f > ans) ans = f, c1 = b1, c2 = b2;
}
void pushup(int rt)
{
IWantToUseALongNameOnlyForHappiness(tree[lson].L, tree[lson].R, tree[rson].L, tree[rson].R, tree[rt].L, tree[rt].R);
}
void build(int rt, int l, int r)
{
tree[rt].l = l; tree[rt].r = r;
if(l == r)
{
tree[rt].L = tree[rt].R = l;
return;
}
int mid = (l + r) >> 1;
build(lson, l, mid);
build(rson, mid+1, r);
pushup(rt);
}
void query(int rt, int l, int r, int &L, int &R)
{
//printf("rt = %d\n", rt);
if(l <= tree[rt].l && tree[rt].r <= r)
{
IWantToUseALongNameOnlyForHappiness(L, R, tree[rt].L, tree[rt].R, L, R);
return;
}
int mid = (tree[rt].l + tree[rt].r) >> 1;
if(l <= mid) query(lson, l, r, L, R);
if(r > mid) query(rson, l, r, L, R);
}
int main()
{
n = read();
for(int i=1; i<n; i++)
{
int x = read(), y = read(), w = read();
add(x, y, w); add(y, x, w);
}
find_heavy_edge(1, 1, 1);
connect_heavy_dege(1, 1);
build(1, 1, n);
//printf("111\n");
m = read();
while(m--)
{
int a = read(), b = read(), c = read(), d = read();
int e = a, f = b, g = c, h = d;
query(1, a, b, e, f);
//printf("222\n");
query(1, c, d, g, h);
ans = max(get_dis(e, g), get_dis(e, h));
ans = max(ans, max(get_dis(f, g), get_dis(f, h)));
printf("%lld\n", ans);
}
return 0;
}
from Catherine_leah
T4:给你一棵树,要求把节点分成若干类,每类的权值就是这类点里面的点权最大的,要求每一类里的节点不能有祖先关系,求能构造出的若干类权值加和的最小值
(1)解法:从最简单的考虑,如果是一条1不是根的链,那么一定是从两条链里面每次选一个,取最大的作为类的权值,另一个一定跟在里面是最优的;类推到三个,就是1+1+1->2+1 2+1->3,类推到一颗子树,就是一样的,从叶子往上合并
(2)复杂度分析:在把rt的儿子依次合并的时候,必须启发式合并,小的往大的上合并,复杂度才是对的,可以用标号指向大根堆的下标,swap一下下标就可以,所以复杂度分析真的很重要!!!
int n,M[N],head[N],tot,du[N],tp[N],cn;
int id[N];
struct DUG
{
int to,nxt;
}e[N<<1];
inline void Add(int x,int y)
{
e[++tot].to=y,e[tot].nxt=head[x],head[x]=tot;
}
priority_queue<int>st[N];//最多n个节点
inline void Goback(int x,int fi)
{
cn=0;
if(st[id[x]].size()>st[id[fi]].size())swap(id[x],id[fi]);//让父亲是大的,id[x]是小的
while(!st[id[x]].empty())
{
int xi=st[id[fi]].top(),yi=st[id[x]].top();
tp[++cn]=max(xi,yi);
st[id[fi]].pop(),st[id[x]].pop();
}
_f(i,1,cn)st[id[fi]].push(tp[i]);
}
inline void dfs(int x)
{
for(rint i=head[x];i;i=e[i].nxt)
{
int to=e[i].to;
dfs(to);
Goback(to,x);
}
st[id[x]].push(M[x]);//为了儿子不和父亲在一个内存条里出现,必须最后放进去
}
int main()
{
//freopen("qiandao4.in","r",stdin);
// freopen("dfs.txt","w",stdout);
n=re();
_f(i,1,n)M[i]=re(),id[i]=i;
_f(i,2,n)
{
int x=re();Add(x,i);
}
dfs(1);
ll ans=0;
while(!st[id[1]].empty())
{
ans=ans+(ll)st[id[1]].top();
st[id[1]].pop();
}
chu("%lld",ans);
return 0;
}