loj #2322. 「清華集訓 2017」Hello world!

題面

題意

給出一棵樹,要求支持兩種操作:
1.給出u,v,從u開始每次向v條k條邊(如果到v的距離小於k則直接到達v),對所有經過的點的權值開根。
2.給出u,v,從u開始每次向v條k條邊(如果到v的距離小於k則直接到達v),求所有經過的點的權值和。

做法

根據每次跳的長度k進行分塊,令塊大小爲S。
然後對於1~S中的每一個數i建一個森林,每個點的父親是這個點在原樹上與它深度差i的祖先,並對森林中的每棵樹都用樹狀數組維護其DFS序的前綴點權和,並且用並查集維護每個點在這棵樹的所有祖先中(包括自己)點權不爲1,深度最大的點(優化開根)。

對於p到q的路徑,可以拆成:
這裏寫圖片描述
其中p->pu和q’->qu上每隔k個就是一個需要處理的點,再對q單獨處理,要注意q’->qu這段長度爲0和q爲lca 的情況。

對於操作1:
對於小於等於S的k,在新建出的樹上跳(利用並查集優化),對這些數開根。
對於大於S的k,暴力向上跳並開根。
若開根之後數字爲1,修改這個點在所有樹上的並查集和樹狀數組。

對於操作2:
對於小於等於S的k,在新建出的樹上樹鏈剖分。
對於大於S的k,暴力向上跳並求和。

需要注意的是如果某個點的點權初始爲1則它在並查集中的初始父節點爲其父親。

代碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#define ll long long
#define LG 15
#define MKS 45
#define N 50010
using namespace std;

ll n,m,num[N],first[N],fa[N][20],deep[N],mx[N],sn[N],bb,tt,ans,KS;
struct Bn
{
    ll to,next;
}bn[N<<1];

inline void kg(ll u);
struct Tree
{
    ll fa[N],up[N],in[N],sz[N],top[N];
    vector<ll>son[N],gen;

    ll fu(ll u){return u==up[u]?u:up[u]=fu(up[u]);}
    inline ll lb(ll u){return u&(-u);}
    inline void add(ll u,ll v){for(;u<=n;u+=lb(u)) sz[u]+=v;}
    inline ll ask(ll u){ll res=0;for(;u;u-=lb(u)) res+=sz[u];return res;}

    inline void insert(ll u,ll v)
    {
        fa[u]=v;
        up[u]=u;
        if(!v) gen.push_back(u);
        else son[v].push_back(u);
    }

    inline void chg(ll u)
    {
        up[u]=fu(fa[u]);
    }

    ll dfs(ll now)
    {
        ll i,j,res=1,tmp,mx=0;
        sn[now]=0;
        for(i=0;i<son[now].size();i++)
        {
            res+=tmp=dfs(son[now][i]);
            if(tmp>mx)
            {
                mx=tmp;
                sn[now]=son[now][i];
            }
        }
        return res;
    }

    void Dfs(ll now)
    {
        in[now]=++tt;
        add(tt,num[now]);
        if(sn[now])
        {
            top[sn[now]]=top[now];
            Dfs(sn[now]);
        }
        else return;
        ll i,j;
        for(i=0;i<son[now].size();i++)
        {
            if(son[now][i]==sn[now]) continue;
            top[son[now][i]]=son[now][i];
            Dfs(son[now][i]);
        }
    }

    inline void init()
    {
        ll i,j;
        tt=0;
        for(i=0;i<gen.size();i++)
        {
            top[gen[i]]=gen[i];
            dfs(gen[i]);
            Dfs(gen[i]);
        }
        for(i=1;i<=n;i++) if(num[i]==1) chg(i);
    }

    inline void work(ll u,ll v)
    {
        for(u=fu(u);deep[u]>=deep[v];u=fu(fa[u]))
        {
            kg(u);
        }
    }

    inline ll sum(ll u,ll v)
    {
        ll res=0;
        for(;top[u]!=top[v];)
        {
            res+=ask(in[u])-ask(in[top[u]]-1);
            u=fa[top[u]];
        }
        res+=ask(in[u])-ask(in[v]-1);
        return res;
    }
}tree[MKS+10];

inline void add(ll u,ll v)
{
    bb++;
    bn[bb].to=v;
    bn[bb].next=first[u];
    first[u]=bb;
}

inline ll get(ll u,ll v)
{
    ll i=mx[v];
    v=deep[u]-v;
    if(v<=0) return 0;
    for(;deep[u]!=v;i--)
    {
        if(deep[fa[u][i]]>=v) u=fa[u][i];
    }
    return u;
}

inline ll lca(ll u,ll v)
{
    if(deep[u]<deep[v]) swap(u,v);
    ll i;
    u=get(u,deep[u]-deep[v]);
    for(i=mx[deep[u]];i>=0;i--)
    {
        if(fa[u][i]!=fa[v][i])
        {
            u=fa[u][i];
            v=fa[v][i];
        }
    }
    if(u!=v) return fa[u][0];
    return u;
}

void dfs(ll now,ll last)
{
    ll p,q;
    for(p=first[now];p!=-1;p=bn[p].next)
    {
        if(bn[p].to==last) continue;
        fa[bn[p].to][0]=now;
        deep[bn[p].to]=deep[now]+1;
        dfs(bn[p].to,now);
    }
}

inline void kg(ll u)
{
    if(num[u]==1) return;
    ll i,j,v,t;
    v=sqrt(num[u]);
    t=v-num[u];
    for(i=1;i<=KS;i++) tree[i].add(tree[i].in[u],t);
    if(v==1)
    {
        for(i=1;i<=KS;i++)
        {
            tree[i].chg(u);
        }
    }
    num[u]=v;
}

int main()
{
    memset(first,-1,sizeof(first));
    ll i,j,p,q,o,z,l,pu,qu,qy,t;
    cin>>n;
    KS=min((ll)sqrt(n),(ll)MKS);
    mx[0]=-1;
    for(i=1;i<=n;i++)
    {
        scanf("%lld",&num[i]);
        mx[i]=mx[i/2]+1;
    }
    for(i=1;i<n;i++)
    {
        scanf("%lld%lld",&p,&q);
        add(p,q);
        add(q,p);
    }
    deep[1]=1;
    dfs(1,-1);
    for(j=1;j<=LG;j++)
    {
        for(i=1;i<=n;i++)
        {
            fa[i][j]=fa[fa[i][j-1]][j-1];
        }
    }

    for(i=1;i<=n;i++)
    {
        p=i;
        for(j=1;j<=KS;j++)
        {
            p=fa[p][0];
            tree[j].insert(i,p);
        }
    }
    for(i=1;i<=KS;i++) tree[i].init();
//  return 0;

    cin>>m;
    for(i=1;i<=m;i++)
    {
        scanf("%lld%lld%lld%lld",&z,&p,&q,&o);
        qy=q;
        l=lca(p,q);
        t=deep[p]-deep[l];
        t=t/o*o;
        pu=get(p,t);
        t=o-(deep[p]-deep[l]-t);
        qu=t=deep[l]+t;
        if(deep[q]>t)
        {
            t+=(deep[q]-t-1)/o*o;
            q=get(q,deep[q]-t);
            qu=get(q,deep[q]-qu);
        }
        else q=qu=0;
        if(!z)
        {
            if(pu!=qy)
                kg(qy);
            if(o>KS)
            {
                for(;deep[p]>=deep[pu];p=get(p,t))
                {
                    kg(p);
                    t=tree[1].fu(fa[p][0]);
                    t=(deep[p]-deep[t]+o-1)/o*o;
                }
                if(q)
                {
                    for(;deep[q]>=deep[qu];q=get(q,t))
                    {
                        kg(q);
                        t=tree[1].fu(fa[q][0]);
                        t=(deep[q]-deep[t]+o-1)/o*o;
                    }
                }
            }
            else
            {
                tree[o].work(p,pu);
                if(q) tree[o].work(q,qu);
            }
        }
        else
        {
            ans=pu!=qy?num[qy]:0;
            if(o>KS)
            {
                for(;;p=get(p,o))
                {
                    ans+=num[p];
                    if(p==pu) break;
                }
                if(q)
                {
                    for(;;q=get(q,o))
                    {
                        ans+=num[q];
                        if(q==qu) break;
                    }
                }
            }
            else
            {
                ans+=tree[o].sum(p,pu);
                if(q)
                    ans+=tree[o].sum(q,qu);
            }
            printf("%lld\n",ans);
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章