1. 程式人生 > >CF916E Jamie and Tree 解題報告

CF916E Jamie and Tree 解題報告

三種 its roc all 輸入 des mat tween scribe

CF916E Jamie and Tree

題意翻譯

有一棵\(n\)個節點的有根樹,標號為\(1-n\),你需要維護一下三種操作

1.給定一個點\(v\),將整顆樹的根變為\(v\)

2.給定兩個點\(u, v\),將\(lca(u, v)\)所在的子樹都加上\(x\)

3.給定一個點\(v\),你需要回答以\(v\)所在的子樹的權值和

輸入輸出格式

輸入格式:

The first line of input contains two space-separated integers \(n\) and \(q\) \((1<=n<=10^{5},1<=q<=10^{5})\)

— the number of vertices in the tree and the number of queries to process respectively.

The second line contains \(n\) space-separated integers\(a_{1},a_{2},...,a_{n}\)(\(-10^{8}<=a_{i}<=10^{8}\)) — initial values of the vertices.

Next \(n-1\) lines contains two space-separated integers\(u_{i},v_{i}\)

(\(1<=u_{i},v_{i}<=n\)) describing edge between vertices \(u_{i}\)and \(v_{i}\)in the tree.

The following \(q\) lines describe the queries.

Each query has one of following formats depending on its type:

\(1\ v\) ( \(1<=v<=n\) ) for queries of the first type.

\(2\ u\ v\ x\) ( \(1<=u,v<=n,-10^{8}<=x<=10^{8}\)

) for queries of the second type.

\(3\ v ( 1<=v<=n )\) for queries of the third type.

All numbers in queries‘ descriptions are integers.

The queries must be carried out in the given order. It is guaranteed that the tree is valid.

輸出格式:

For each query of the third type, output the required answer. It is guaranteed that at least one query of the third type is given by Jamie.


強制換根==分類討論

如果在考場上比較難寫就考慮暴力吧

首先介紹幾點可能比較常見的
1.換根後的\(lca(u,v)\)

其實求法比較多,介紹一種

\(z1=lca(u,root),z2=lca(v,root)\),如果\(z1==z2\),則\(lca(u,v)\)不變,否則\(lca(u,v)\)\(z1,z2\)中深度較深的那個

2.一個點是否在某點的子樹裏

bool in(int x)
{
    if(dfn[x]>=dfn[root]&&dfn[x]<=dfn[root]+siz[root]-1) return true;
    return false;
}

對於這道題,我們在分類討論上稍稍用一點容斥原理即可


Code:

#include <cstdio>
#define ll long long
#define ls id<<1
#define rs id<<1|1
const int N=100010;
int head[N],to[N<<1],Next[N<<1],cnt;
void add(int u,int v)
{
    to[++cnt]=v;Next[cnt]=head[u];head[u]=cnt;
}
int f[N][20],dfn[N],dep[N],siz[N],ha[N],dfs_clock,root;
void dfs(int now)
{
    dfn[now]=++dfs_clock;
    ha[dfs_clock]=now;
    siz[now]++;
    for(int i=head[now];i;i=Next[i])
    {
        int v=to[i];
        if(f[now][0]!=v)
        {
            dep[v]=dep[now]+1;
            f[v][0]=now;
            dfs(v);
            siz[now]+=siz[v];
        }
    }
}
ll sum[N<<2],lazy[N<<2],dat[N];
int n,q;
void updata(int id)
{
    sum[id]=sum[ls]+sum[rs];
}
void push_down(int id,ll L,ll R)
{
    if(!lazy[id]) return;
    if(L!=R)
    {
        ll mid=L+R>>1;
        sum[ls]+=(mid+1-L)*lazy[id];
        sum[rs]+=(R-mid)*lazy[id];
        lazy[ls]+=lazy[id];
        lazy[rs]+=lazy[id];
    }
    lazy[id]=0;
}
void build(int id,int l,int r)
{
    if(l==r)
    {
        sum[id]=dat[ha[l]];
        return;
    }
    int mid=l+r>>1;
    build(ls,l,mid);
    build(rs,mid+1,r);
    updata(id);
}
void change(int id,ll l,ll r,ll L,ll R,ll delta)
{
    push_down(id,L,R);
    if(l==L&&r==R)
    {
        sum[id]+=(R+1-L)*delta;
        lazy[id]+=delta;
        return;
    }
    ll mid=L+R>>1;
    if(r<=mid) change(ls,l,r,L,mid,delta);
    else if(l>mid) change(rs,l,r,mid+1,R,delta);
    else change(ls,l,mid,L,mid,delta),change(rs,mid+1,r,mid+1,R,delta);
    updata(id);
}
ll query(int id,ll l,ll r,ll L,ll R)
{
    push_down(id,L,R);
    if(l==L&&r==R) return sum[id];
    ll mid=L+R>>1;
    if(r<=mid) return query(ls,l,r,L,mid);
    else if(l>mid) return query(rs,l,r,mid+1,R);
    else return query(ls,l,mid,L,mid)+query(rs,mid+1,r,mid+1,R);
}
void init()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++) scanf("%lld",dat+i);
    for(int u,v,i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v),add(v,u);
    }
    dep[1]=1;
    dfs(root=1);
    for(int j=1;j<=18;j++)
        for(int i=1;i<=n;i++)
            f[i][j]=f[f[i][j-1]][j-1];
    build(1,1,n);
}
void Swap(int &x,int &y)
{
    int tmp=x;
    x=y;
    y=tmp;
}
int LCA0(int x,int y)//原樹的LCA
{
    if(dep[x]<dep[y]) Swap(x,y);
    for(int i=18;~i;i--)
        if(dep[f[x][i]]>=dep[y])
            x=f[x][i];
    if(x==y) return x;
    for(int i=18;~i;i--)
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    return f[x][0];
}
int LCA(int x,int y)//換根後的LCA
{
    int z1=LCA0(x,root),z2=LCA0(y,root);
    if(z1==z2) return LCA0(x,y);
    else return dep[z1]>dep[z2]?z1:z2;
}
bool in(int x)//詢問x是否在以root為根的子樹裏
{
    if(dfn[x]>=dfn[root]&&dfn[x]<=dfn[root]+siz[root]-1) return true;
    return false;
}
int querys(int x)//詢問根到x位置上x的那個兒子
{
    int y=root;
    for(int i=18;~i;i--)
        if(dep[f[y][i]]>dep[x])
            y=f[y][i];
    return y;
}
void modify(int x,ll delta)//給以x為根的子樹加上delta
{
    if(in(x))
    {
        if(x!=root) change(1,dfn[x],dfn[x]+siz[x]-1,1,n,delta);
        else change(1,1,n,1,n,delta);
        return;
    }
    int son=querys(x);
    if(f[son][0]==x)
    {
        change(1,1,n,1,n,delta);
        change(1,dfn[son],dfn[son]+siz[son]-1,1,n,-delta);
    }
    else
        change(1,dfn[x],dfn[x]+siz[x]-1,1,n,delta);
}
ll ask(int x)//詢問新根下子樹權值和
{
    if(in(x))
    {
        if(x!=root) return query(1,dfn[x],dfn[x]+siz[x]-1,1,n);
        return query(1,1,n,1,n);
    }
    int son=querys(x);
    ll ans=0;
    if(f[son][0]==x)
    {
        ans+=query(1,1,n,1,n);
        ans-=query(1,dfn[son],dfn[son]+siz[son]-1,1,n);
    }
    else
        ans=query(1,dfn[x],dfn[x]+siz[x]-1,1,n);
    return ans;
}
void work()
{
    ll x;
    for(int opt,u,v,i=1;i<=q;i++)
    {
        scanf("%d",&opt);
        if(opt==1)
        {
            scanf("%d",&u);
            root=u;
        }
        else if(opt==2)
        {
            scanf("%d%d%lld",&u,&v,&x);
            modify(LCA(u,v),x);
        }
        else
        {
            scanf("%d",&u);
            printf("%lld\n",ask(u));
        }
    }
}
int main()
{
    init();
    work();
    return 0;
}

2018.7.31

CF916E Jamie and Tree 解題報告