暴力写挂

Posted xjqxjq

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了暴力写挂相关的知识,希望对你有一定的参考价值。

题目描述

题解

考虑把式子化一下,因为只有一个式子跟第二棵树有关,所以我们可以考虑把前面的式子化成跟 $ ext{lca}$ 没有关系,即 $frac{1}{2}(dp_u+dp_v+dis(u,v))$ 。因此我们可以利用边分治,每次把两边的点黑白染色,构成虚树,然后做 $ ext{dp}$ 即可。这里要注意 $ ext{lca}$ 要 $O(1)$ 求,虚树构成过程中不能排序,故我们可以一开始就按照第二棵树的dfs排序好,之后分治下去即可。效率: $O(nlogn)$ 。

代码

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=4e5+5,N2=N<<1,N4=N<<2;
int n,m,t=1,fa[22][N2],Lg[N2],d[N],e[N],o,rt,su,hd[N2],sz[N2];
int V[N4],W[N4],nx[N4],b[N],id[N],col[N],tp,S[N],h[2][N],c;
LL dp[N],Dp[N],sm[N],f[2][N],ans=-2e18;
bool vis[N2];vector<int>X[N],Y[N];
void Add(int u,int v,int w){
    X[u].push_back(v);Y[u].push_back(w);
}
void add(int u,int v,int w){
    nx[++t]=hd[u];V[hd[u]=t]=v;W[t]=w;
}
void add(int u,int v){X[u].push_back(v);}
void rebuild(int u,int fr){
    int x=0,z=X[u].size();
    for (int v,w,i=0;i<z;i++){
        v=X[u][i];w=Y[u][i];
        if (v==fr) continue;dp[v]=dp[u]+w;
        if (!x) add(u,v,w),add(v,u,w),x=u;
        else m++,add(x,m,0),add(m,x,0),
            add(m,v,w),add(v,m,w),x=m;
        rebuild(v,u);
    }
}
void dfs(int u,int fr){
    int z=X[u].size();
    fa[0][e[u]=++c]=u;b[id[u]=++t]=u;
    for (int v,w,i=0;i<z;i++){
        v=X[u][i];w=Y[u][i];
        if (v==fr) continue;
        Dp[v]=Dp[u]+w;d[v]=d[u]+1;
        dfs(v,u);fa[0][++c]=u;
    }
}
int Min(int u,int v){return d[u]<d[v]?u:v;}
int qry(int l,int r){
    l=e[l];r=e[r];
    if (l>r) swap(l,r);int i=Lg[r-l+1];
    return Min(fa[i][l],fa[i][r-(1<<i)+1]);
}
void Sz(int u,int fr){
    sz[u]=1;
    for (int v,i=hd[u];i;i=nx[i])
        if (!vis[i>>1] && (v=V[i])!=fr)
            Sz(v,u),sz[u]+=sz[v];
}
void Rt(int u,int fr){
    for (int v,w,i=hd[u];i;i=nx[i])
        if (!vis[i>>1] && (v=V[i])!=fr){
            w=max(sz[v],o-sz[v]);
            if (w<su) rt=i,su=w;Rt(v,u);
        }
}
void find(int u,int fr,LL w,int cl){
    if (u<=n) col[u]=cl,sm[u]=w;
    for (int v,i=hd[u];i;i=nx[i])
        if (!vis[i>>1] && (v=V[i])!=fr)
            find(V[i],u,w+W[i],cl);
}
void ins(int u){
    if (tp<1){S[++tp]=u;return;}
    int x=qry(S[tp],u);
    if (x==S[tp]){S[++tp]=u;return;}
    while(tp>1 && id[S[tp-1]]>=id[x])
        add(S[tp-1],S[tp]),tp--;
    if (x!=S[tp]) add(x,S[tp]),S[tp]=x;
    S[++tp]=u;
}
void get(int u){
    int z=X[u].size();
    f[0][u]=f[1][u]=-2e18;
    if (~col[u]) f[col[u]][u]=dp[u]+sm[u];
    for (int v,i=0;i<z;i++){
        v=X[u][i];get(v);
        for (int j=0;j<2;j++)
            ans=max(ans,f[j][u]+f[!j][v]-2ll*Dp[u]);
        for (int j=0;j<2;j++)
            f[j][u]=max(f[j][u],f[j][v]);
    }
    X[u].clear();
}
void solve(int u,int l,int r){
    Sz(u,0);o=sz[u];rt=0;su=1e9;
    Rt(u,0);if (!rt) return;
    int x=V[rt],y=V[rt^1];vis[rt>>1]=1;
    find(x,y,0,0);find(y,x,W[rt],1);
    if (b[l]!=1) ins(1);
    for (int i=l;i<=r;i++) ins(b[i]);
    while(tp>1) add(S[tp-1],S[tp]),tp--;
    tp=0;get(1);int v[2]={0};
    for (int w,i=l;i<=r;i++)
        w=col[b[i]],h[w][++v[w]]=b[i],col[b[i]]=-1;
    for (int i=0;i<v[0];i++) b[i+l]=h[0][i+1];
    for (int i=0;i<v[1];i++) b[r-i]=h[1][v[1]-i];
    solve(x,l,l+v[0]-1);solve(y,r-v[1]+1,r);
}
int main(){
    cin>>n;m=n;
    for (int i=1,x,y,z;i<n;i++)
        scanf("%d%d%d",&x,&y,&z),
        Add(x,y,z),Add(y,x,z);rebuild(1,0);
    for (int i=1;i<=n;i++)
        X[i].clear(),Y[i].clear(),col[i]=-1;t=0;
    for (int i=1,x,y,z;i<n;i++)
        scanf("%d%d%d",&x,&y,&z),
        Add(x,y,z),Add(y,x,z);dfs(1,0);
    for (int i=2;i<=c;i++) Lg[i]=Lg[i>>1]+1;
    for (int i=c;i;i--)
        for (int j=1;i+(1<<j)<=c+1;j++)
            fa[j][i]=Min(fa[j-1][i],fa[j-1][i+(1<<(j-1))]);
    for (int i=1;i<=n;i++) X[i].clear();
    solve(1,1,n);ans>>=1;
    for (int i=1;i<=n;i++)
        ans=max(ans,dp[i]-Dp[i]);
    cout<<ans<<endl;return 0;
}

 

以上是关于暴力写挂的主要内容,如果未能解决你的问题,请参考以下文章

暴力写挂

[CTSC2018]暴力写挂——边分树合并

[CTSC2018]暴力写挂

uoj#400. CTSC2018暴力写挂(边分治)

UOJ#400. CTSC2018暴力写挂 边分治 线段树合并

做题记录