一道模拟赛题

一道模拟赛题

简要题意:

  • 树,单点修改,求直径,求必须选\(x\)的最长链,卡空间。

分析:

我们先把必须选的那个丢掉,发现他实际上是单点修改\(inf\)然后查树的直径。

  • 如果不卡空间的话我们可以用点分树来搞一搞,不过由于一次修改会影响到很多点不能用堆来维护只能用支持区间修改的线段树,这样空间就开不下了。
  • 我们还有动态\(DP\)
  • 对一条重链维护\(lx\)表示必须选上端点向下延伸的最长链,\(rx\)表示必须选下端点向上延伸的最长链,\(s\)表示点权和,\(mx\)表示这些点及他们轻儿子子树内直径。
  • 然后你会发现转移非常合乎逻辑,然而你要是硬写出\(dp\)式子的话却不太好分析,所以我也不知道动态\(dp\)该用哪种方法。
  • 除此之外还需要维护两个set \(s,t\)\(s\)表示向下轻儿子的链的集合,\(t\)表示轻儿子子树内直径和过当前点的最长链的集合。
  • 修改直接修改,查询直接查询。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <set>
using namespace std;
#define N 100050
#define inf (1ll<<55)
typedef long long ll;
#define db(x) cerr<<#x<<" = "<<x<<endl
#define ls p<<1
#define rs p<<1|1
int head[N],to[N<<1],nxt[N<<1],cnt,n,m,dfn[N],idf[N];
int fa[N],top[N],siz[N],son[N],sson[N];
ll f[N],g[N],a[N];
multiset<ll,greater<ll> >s[N],t[N],all;
multiset<ll,greater<ll> >::iterator it;
struct A {
    ll lx,rx,mx,s;
    A() {}
    A(ll lx_,ll rx_,ll mx_,ll s_) {lx=lx_,rx=rx_,mx=mx_,s=s_;}
    A operator + (const A &u) const {
        return A(max(lx,s+u.lx),max(u.rx,u.s+rx),max(mx,max(u.mx,rx+u.lx)),s+u.s);
    }
}tr[N<<2];
inline void add(int u,int v) {to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt;}
void d1(int x,int y) {
    int i; fa[x]=y; siz[x]=1;
    for(i=head[x];i;i=nxt[i]) if(to[i]!=y) {
        d1(to[i],x); siz[x]+=siz[to[i]];
        if(siz[to[i]]>siz[son[x]]) son[x]=to[i];
    }
}
void d2(int x,int tp) {
    int i;
    top[x]=tp; dfn[x]=++dfn[0]; idf[dfn[0]]=x;
    g[x]=max(0ll,a[x]); s[x].insert(0ll);
    if(son[x]) {
        d2(son[x],tp),sson[x]=sson[son[x]];
    }else {
        sson[x]=x;
    }
    for(i=head[x];i;i=nxt[i]) if(to[i]!=fa[x]&&to[i]!=son[x]) {
        d2(to[i],to[i]);
        s[x].insert(f[to[i]]);
        t[x].insert(g[to[i]]);
        g[x]=max(g[x],f[x]+a[x]+f[to[i]]);
        f[x]=max(f[x],f[to[i]]);
    }
    t[x].insert(g[x]);
    g[x]=max(g[x],f[x]+a[x]+f[son[x]]);
    g[x]=max(g[x],max(*t[x].begin(),g[son[x]]));
    f[x]=max(f[son[x]],f[x]);
    f[x]+=a[x]; f[x]=max(0ll,f[x]);
}
void build(int l,int r,int p) {
    if(l==r) {
        int x=idf[l];
        tr[p]=A(*s[x].begin()+a[x],*s[x].begin()+a[x],*t[x].begin(),a[x]); return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,ls); build(mid+1,r,rs);
    tr[p]=tr[ls]+tr[rs];
}
A query(int l,int r,int x,int y,int p) {
    if(x<=l&&y>=r) return tr[p];
    int mid=(l+r)>>1;
    if(y<=mid) return query(l,mid,x,y,ls);
    else if(x>mid) return query(mid+1,r,x,y,rs);
    else return query(l,mid,x,y,ls)+query(mid+1,r,x,y,rs);
}
void update(int l,int r,int x,int p) {
    if(l==r) {
        int x=idf[l];
        tr[p]=A(*s[x].begin()+a[x],*s[x].begin()+a[x],*t[x].begin(),a[x]);
        return ;
    }
    int mid=(l+r)>>1;
    if(x<=mid) update(l,mid,x,ls);
    else update(mid+1,r,x,rs);
    tr[p]=tr[ls]+tr[rs];
}
void UPD(int x,ll y) {
    all.erase(all.find(a[x]));
    all.insert(y);
    if(s[x].size()==1) {
        t[x].erase(t[x].find(max(0ll,*s[x].begin()+a[x])));
        t[x].insert(max(0ll,*s[x].begin()+y));
    }else {
        it=s[x].begin();
        ll tmp=*it;
        it++;
        tmp+=*it;
        t[x].erase(t[x].find(max(0ll,tmp+a[x])));
        t[x].insert(max(0ll,tmp+y));
    }
    a[x]=y;
    A tt;
    
    while(x) {
        update(1,n,dfn[x],1);
        x=top[x];
        tt=query(1,n,dfn[x],dfn[sson[x]],1);
        if(fa[x]) {
            if(s[fa[x]].size()==1) {
                t[fa[x]].erase(t[fa[x]].find(max(0ll,*s[fa[x]].begin()+a[fa[x]])));
            }else {
                it=s[fa[x]].begin();
                ll tmp=*it;
                it++;
                tmp+=*it;
                t[fa[x]].erase(t[fa[x]].find(max(0ll,tmp+a[fa[x]])));
            }
            t[fa[x]].erase(t[fa[x]].find(g[x]));
            s[fa[x]].erase(s[fa[x]].find(f[x]));
            f[x]=max(0ll,tt.lx);
            g[x]=max(0ll,tt.mx);
            t[fa[x]].insert(g[x]);
            s[fa[x]].insert(f[x]);
            if(s[fa[x]].size()==1) {
                t[fa[x]].insert(max(0ll,*s[fa[x]].begin()+a[fa[x]]));
            }else {
                it=s[fa[x]].begin();
                ll tmp=*it;
                it++;
                tmp+=*it;
                t[fa[x]].insert(max(0ll,tmp+a[fa[x]]));
            }
        }else {
            f[x]=max(0ll,tt.lx);
            g[x]=max(0ll,tt.mx);
        }
        x=fa[x];
    }
}
ll Query() {
    if(*all.begin()<0) return *all.begin();
    else return query(1,n,dfn[1],dfn[sson[1]],1).mx;
}
int main() {
    scanf("%d%d",&n,&m);
    int i,x,y,opt;
    for(i=1;i<n;i++) {
        scanf("%d%d",&x,&y); add(x,y); add(y,x);
    }
    for(i=1;i<=n;i++) scanf("%lld",&a[i]),all.insert(a[i]);
    d1(1,0); d2(1,1); build(1,n,1);
    while(m--) {
        scanf("%d",&opt);
        if(opt==1) {
            printf("%lld\n",Query());
        }else if(opt==2) {
            scanf("%d",&x);
            ll tmp=a[x];
            UPD(x,inf);
            printf("%lld\n",Query()-(inf-tmp));
            UPD(x,tmp);
        }else {
            scanf("%d%d",&x,&y);
            UPD(x,y);
        }
    }
}
/*
10 10
2 1
3 1
4 2
5 1
6 3
7 2
8 3
9 2
10 2
4 -6 -10 -4 10 5 -9 -6 4 4
2 4
1
1
2 6
1
2 4
1
1
1
3 7 3
*/

转载于:https://www.cnblogs.com/suika/p/10165778.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值