51nod1297 管理二叉树

考虑如何建树。按顺序加入点,那么新点要不然是前驱的右儿子,要不然是后继的左儿子,否则权值在新点和它父亲中间的点只能做新点的儿子,而这是不可能的。而这两个节点,后插入的一定把先插入的那个位置占掉了,这样新点的插入位置就是惟一的。找前驱后继可以用平衡树维护。
建好树以后就是动态点分治裸题了。

#include<cstdio>
#include<set>
#include<vector>
#include<algorithm>
using namespace std;
#define LL long long
const int maxn=200010;
int a[maxn],lson[maxn],rson[maxn],pos[maxn],dep[maxn],mnd[maxn][20],lg[maxn],
size[maxn],fa[maxn],val[maxn],vis[maxn],
n,clo,R;
LL sum[maxn],sumfa[maxn],ans;
vector<int> to[maxn];
set<int> s;
void dfs0(int u,int fa)
{
    mnd[pos[u]=++clo][0]=dep[u];
    for (int v:to[u])
        if (v!=fa)
        {
            dep[v]=dep[u]+1;
            dfs0(v,u);
            mnd[++clo][0]=dep[u];
        }
}
int dfs2(int u,int fa,int S)
{
    int ret=-1,x;
    val[u]=0;
    size[u]=1;
    for (int v:to[u])
        if (!vis[v]&&v!=fa)
        {
            x=dfs2(v,u,S);
            if (ret==-1||val[x]<val[ret]) ret=x;
            size[u]+=size[v];
            val[u]=max(val[u],size[v]);
        }
    val[u]=max(val[u],S-size[u]);
    if (ret==-1||val[u]<val[ret]) ret=u;
    return ret;
}
void dfs3(int u,int fa)
{
    size[u]=1;
    for (int v:to[u])
        if (!vis[v]&&v!=fa)
        {
            dfs3(v,u);
            size[u]+=size[v];
        }
}
int dfs1(int u,int S)
{
    int r=dfs2(u,-1,S),r1;
    vis[r]=1;
    dfs3(r,-1);
    for (int v:to[r])
        if (!vis[v])
        {
            r1=dfs1(v,size[v]);
            fa[r1]=r;
        }
    vis[r]=0;
    return r;
}
int dis(int u,int v)
{
    int x=pos[u],y=pos[v],k;
    if (x>y) swap(x,y);
    k=lg[y-x+1];
    return dep[u]+dep[v]-2*min(mnd[x][k],mnd[y-(1<<k)+1][k]);
}
void add(int u)
{
    int d;
    for (int i=u,j=0;i;j=i,i=fa[i])
        ans+=sum[i]-sumfa[j]+(LL)dis(u,i)*(size[i]-size[j]);
    for (int i=u,j=0;i;j=i,i=fa[i])
    {
        d=dis(u,i);
        sum[i]+=d;
        if (j) sumfa[j]+=d;
        size[i]++;
    }
}
int main()
{
    //freopen("e.in","r",stdin);
    set<int>::iterator it;
    scanf("%d",&n);
    scanf("%d",&a[1]);
    s.insert(a[1]);
    for (int i=2;i<=n;i++)
    {
        scanf("%d",&a[i]);
        it=s.upper_bound(a[i]);
        if (it==s.end()||lson[*it]) rson[*(--it)]=a[i];
        else lson[*it]=a[i];
        s.insert(a[i]);
    }
    for (int i=1;i<=n;i++)
    {
        if (lson[i]) to[i].push_back(lson[i]),to[lson[i]].push_back(i);
        if (rson[i]) to[i].push_back(rson[i]),to[rson[i]].push_back(i);
    }
    dfs0(1,-1);
    for (int k=1;(1<<k)<=clo;k++) lg[1<<k]=k;
    for (int i=3;i<=clo;i++)
        if (!lg[i]) lg[i]=lg[i-1];
    for (int k=1;k<=lg[clo];k++)
        for (int i=1;i+(1<<k)-1<=clo;i++)
            mnd[i][k]=min(mnd[i][k-1],mnd[i+(1<<(k-1))][k-1]);
    R=dfs1(1,n);
    for (int i=1;i<=n;i++) size[i]=0;
    for (int i=1;i<=n;i++)
    {
        add(a[i]);
        printf("%lld\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值