[Codeforces 966]E - May Holidays 分块+虚树

对询问分块,把每 n n 个询问放在一起处理,并对这些询问涉及到的点建出一棵虚树。
把一个点的反转看成是其到根的路径上的每个点 ti t i +1 + 1 1 − 1 ,那么虚树上一条边的 ti t i 变化都是相同的。我们在处理每一块之前重构所有的 ti t i ,然后对于每条边维护所有没有去度假的人的 ti t i ,并使之有序且要把 ti t i 相等的合并起来,然后只需要再维护一个指针指向正负分界点,对于每个询问就只需要移动 O(n) O ( n ) 个指针就能更新答案了。要注意虚树上的关键点需要单独考虑。
使用桶排序就可以做到 O(nn) O ( n n ) 的复杂度了,实测块大小开到 500 500 600 600 比较好。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define PB push_back
#define MP make_pair
#define pii pair<int,int>
#define fs first
#define sc second 
#define N 100010
#define abs(x) (x<0?-(x):x)
using namespace std;
const int B=600;
int n,m,tote,tim,ans,t[N],r[N],q[N],to[N],nxt[N],con[N],dfn[N],fa[20][N],dep[N],u[N],st[N],id[N],key[N];
vector<int> buc[N<<1];
bool b[N];
int read()
{
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
struct node
{
    int hd,pnt,sum,dx;
    vector<pii> s;
    void clr()
    {
        hd=pnt=sum=dx=0;
        s.clear();
    }
}g[N];
void ins(int x,int y)
{
    to[++tote]=y;nxt[tote]=con[x];con[x]=tote;
}
void dfs(int v,int d)
{
    dfn[v]=++tim;dep[v]=d;
    for(int p=con[v];p;p=nxt[p])
        dfs(to[p],d+1);
}
int lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int k=19;k>=0;k--) if(dep[fa[k][x]]>=dep[y]) x=fa[k][x];
    if(x==y) return x;
    for(int k=19;k>=0;k--) if(fa[k][x]!=fa[k][y]) x=fa[k][x],y=fa[k][y];
    return fa[0][x];
}
int rec(int v)
{
    int re=b[v];
    for(int p=con[v];p;p=nxt[p])
        re+=rec(to[p]);
    r[v]=t[v]-re;
    return re;  
}
bool cmp(int x,int y)
{
    return dfn[x]<dfn[y];
}
void work(int L,int R)
{
    rec(1);
    for(int i=L;i<=R;i++)
        u[i-L]=q[i];
    sort(u,u+R-L+1,cmp);
    int top=1,num=1;st[1]=key[1]=1;
    for(int i=0;i<=R-L;i++)
    {
        int LCA=lca(u[i],st[top]);
        while(dep[LCA]<dep[st[top]])
        {
            if(dep[st[top-1]]<=dep[LCA]) 
            {
                g[st[top--]].hd=LCA;

                if(st[top]!=LCA) st[++top]=LCA,key[++num]=LCA;
                break;
            }
            g[st[top]].hd=st[top-1];
            top--;
        }
        if(st[top]!=u[i]) st[++top]=u[i],key[++num]=u[i];
    }
    for(;top>1;top--) g[st[top]].hd=st[top-1];

    memset(id,0,sizeof(id));

    for(int i=1;i<=num;i++)
        for(int p=fa[0][key[i]];p!=g[key[i]].hd;p=fa[0][p]) 
            id[p]=key[i];       
    for(int i=0;i<=(n<<1);i++)
        buc[i].clear();
    for(int i=1;i<=n;i++)
        if(!b[i]) buc[r[i]+n].PB(i);
    for(int i=0;i<=(n<<1);i++)
        for(int j=0;j<buc[i].size();j++)
            if(id[buc[i][j]])
            {
                int o=id[buc[i][j]],sz=g[o].s.size();
                if(sz&&g[o].s[sz-1].fs==i-n) g[o].s[sz-1].sc++;
                else g[o].s.PB(MP(i-n,1));

            }       
    for(int i=1;i<=num;i++)
        for(int k=key[i];g[k].pnt<g[k].s.size()&&g[k].s[g[k].pnt].fs<0;g[k].pnt++) ;

    for(int i=L;i<=R;i++)
    {   
        b[q[i]]^=1;
        if(!b[q[i]])
        {
            r[q[i]]++;
            if(r[q[i]]<0) ans++;
            for(int p=q[i];g[p].hd;)
            {
                g[p].dx++;
                //cout<<g[p].dx<<' '<<g[p].pnt<<endl;
                if(g[p].pnt&&g[p].s[g[p].pnt-1].fs+g[p].dx>=0) g[p].pnt--,ans-=g[p].s[g[p].pnt].sc;
                p=g[p].hd;
                r[p]++;
                if(b[p]==0&&r[p]==0) ans--;
            }
        }
        else
        {
            if(r[q[i]]<0) ans--;
            r[q[i]]--;
            for(int p=q[i];g[p].hd;)
            {
                g[p].dx--;
                if(g[p].pnt<g[p].s.size()&&g[p].s[g[p].pnt].fs+g[p].dx<0) ans+=g[p].s[g[p].pnt].sc,g[p].pnt++;
                p=g[p].hd;
                r[p]--;
                if(b[p]==0&&r[p]==-1) ans++;
            }
        }

        printf("%d ",ans);

    }
    for(int i=1;i<=num;i++)
        g[key[i]].clr();

}
int main()
{
    n=read();m=read();
    for(int i=2;i<=n;i++)
        ins(fa[0][i]=read(),i);
    for(int k=1;(1<<k)<n;k++)
        for(int i=1;i<=n;i++)
            fa[k][i]=fa[k-1][fa[k-1][i]];

    for(int i=1;i<=n;i++)
        t[i]=read();

    for(int i=1;i<=m;i++)
        q[i]=read(),q[i]=abs(q[i]); 
    dfs(1,1);   
    for(int i=1;i<=m;i+=B)
        work(i,min(i+B-1,m));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值