【CF966E】May Holidays-分块+虚树

测试地址:May Holidays
题目大意:一个n个人的公司,除了1号外每个人都有一个直属上司,uv的下属当且仅当u的直属上司是vu的直属上司是v的下属。每个人都有一个承受阈值ti,当一个人的下属中有严格大于ti个人休假而他没有休假时,他就会不开心。m次操作(改动一个人的休假状态),求每次操作后不开心的人数。
做法:本题需要用到分块+虚树。
思来想去好像没有什么奇妙的数据结构能在O(nlogn)O(nlog2n)内解决这样的问题,于是想到分块。
考虑对询问分块,使用这种方法的条件是,任意一个前缀内询问的影响可以在O(n)时间内求出,而处理单独一个块内询问的复杂度应该为均摊一次O(B)(其中B为块大小)。显然第一个条件可以满足,打标记再扫一遍即可。而对于第二个条件,我们可以先构建所有询问点的链并,实际上就是一棵虚树,只不过1号点一定要存在在虚树中(因为是链并),显然构建的复杂度是O(BlogB)。然后对于虚树上相邻的两个点之间的那些点,受到块内询问的影响都是相同的,于是考虑预处理出每一对相邻点之间链上那些点的一些值。令wi为点i在该块询问之前的询问中受到的影响,newi为点i在处理当前询问时点i所受到的新的影响,那么显然当wi+newi>ti且点i没有被标记(即不处于休假状态)时,点i对答案有贡献,上式可以写成newi>tiwi,于是我们只需对每一条虚树上的边,求出链上所有没被标记的点的tiwi,排序后存下来,那么在newi变化时,因为一次最多变化1,所以指针最多移动一次,而在虚树上更新newi的次数是一次O(B)。那么就能达到预处理O(nlogn)(实际上可以用基数排序优化到O(n),但懒得写了),一个询问均摊O(B)的时间复杂度了。这就符合第二个条件了。
于是我们就解决了本题,时间复杂度为O(n2Blogn+nB),实测当B=2m时可以通过此题。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
int n,m,t[100010],op[100010];
int fa[100010][20]={0},tot=0,first[100010]={0},dep[100010]={0};
int tim=0,pos[100010],w[100010],ans;
int p[100010],totp,top,st[100010],pre[100010];
int totsiz,L[100010],R[100010],to[100010],s[100010],cnt[100010];
int news[100010],newsp[100010];
bool vis[100010]={0};
struct edge
{
    int v,next;
}e[100010];

void insert(int a,int b)
{
    e[++tot].v=b;
    e[tot].next=first[a];
    first[a]=tot;
}

void dfs(int v)
{
    pos[v]=++tim;
    dep[v]=dep[fa[v][0]]+1;
    for(int i=first[v];i;i=e[i].next)
        dfs(e[i].v);
}

void count(int v)
{
    w[v]=0;
    for(int i=first[v];i;i=e[i].next)
    {
        count(e[i].v);
        w[v]+=w[e[i].v];
        if (vis[e[i].v]) w[v]++;
    }
    if (!vis[v]&&w[v]>t[v]) ans++;
}

int lca(int a,int b)
{
    if (dep[a]<dep[b]) swap(a,b);
    for(int i=18;i>=0;i--)
        if (dep[fa[a][i]]>=dep[b])
            a=fa[a][i];
    if (a==b) return a;
    for(int i=18;i>=0;i--)
        if (fa[a][i]!=fa[b][i])
            a=fa[a][i],b=fa[b][i];
    return fa[a][0];
}

bool cmp(int a,int b)
{
    return pos[a]<pos[b];
}

bool cmpw(int a,int b)
{
    return a<b;
}

void findup(int x,int y)
{
    if (x==y) return;
    if (!vis[x]) s[++totsiz]=t[x]-w[x];
    findup(fa[x][0],y);
}

void build()
{
    int newsiz=totp;
    sort(p+1,p+totp+1,cmp);
    for(int i=1;i<totp;i++)
        p[++newsiz]=lca(p[i],p[i+1]);
    p[++newsiz]=1;
    totp=newsiz,newsiz=0;
    sort(p+1,p+totp+1,cmp);
    for(int i=1;i<=totp;i++)
        if (i==1||p[i]!=p[i-1])
            p[++newsiz]=p[i];
    totp=newsiz;

    top=0;
    for(int i=1;i<=totp;i++)
    {
        while(top&&lca(st[top],p[i])!=st[top]) top--;
        if (top) pre[p[i]]=st[top];
        else pre[p[i]]=0;
        st[++top]=p[i];
    }

    totsiz=0;
    for(int i=2;i<=totp;i++)
    {
        int v=p[i],siz=totsiz;
        L[v]=totsiz+1;
        findup(fa[v][0],pre[v]);
        R[v]=totsiz;
        totsiz=siz;
        if (L[v]>R[v]) continue;

        to[v]=L[v]-1;
        sort(s+totsiz+1,s+R[v]+1,cmpw);
        for(int j=L[v];j<=R[v];j++)
        {
            if (j==L[v]||s[j]!=s[j-1])
            {
                s[++totsiz]=s[j];
                if (s[j]<0) to[v]=totsiz;
                cnt[totsiz]=1;
            }
            else cnt[totsiz]++;
        }
        R[v]=totsiz;
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=2;i<=n;i++)
    {
        scanf("%d",&fa[i][0]);
        insert(fa[i][0],i);
    }
    for(int i=1;i<=18;i++)
        for(int j=1;j<=n;j++)
            fa[j][i]=fa[fa[j][i-1]][i-1];
    dfs(1);

    for(int i=1;i<=n;i++)
        scanf("%d",&t[i]);
    for(int i=1;i<=m;i++)
        scanf("%d",&op[i]);
    int B=(int)(sqrt(m)+1)<<1;
    for(int i=1;i<=m;i+=B)
    {
        int l=i,r=min(m,i+B-1);

        ans=0;
        memset(vis,0,sizeof(vis));
        for(int j=1;j<l;j++)
            vis[abs(op[j])]=!vis[abs(op[j])];
        count(1);

        totp=0;
        for(int j=l;j<=r;j++)
            p[++totp]=abs(op[j]);
        build();

        memset(news,0,sizeof(news));
        memset(newsp,0,sizeof(newsp));
        for(int j=l;j<=r;j++)
        {
            int v=abs(op[j]),add=(op[j]>0)?1:-1;
            if (!vis[v]&&newsp[v]>t[v]-w[v]) ans--;
            vis[v]=!vis[v];
            if (!vis[v]&&newsp[v]>t[v]-w[v]) ans++;
            while(v)
            {
                if (v!=abs(op[j])) newsp[v]+=add;
                news[v]+=add;
                if (v!=abs(op[j]))
                {
                    if (!vis[v]&&newsp[v]-add<=t[v]-w[v]&&newsp[v]>t[v]-w[v])
                        ans++;
                    if (!vis[v]&&newsp[v]-add>t[v]-w[v]&&newsp[v]<=t[v]-w[v])
                        ans--;
                }
                if (L[v]>R[v]||!pre[v])
                {
                    v=pre[v];
                    continue;
                }
                if (add>0)
                {
                    if (to[v]!=R[v]&&news[v]>s[to[v]+1])
                    {
                        to[v]++;
                        ans+=cnt[to[v]];
                    }
                }
                else if (to[v]!=L[v]-1)
                {
                    if (news[v]<=s[to[v]])
                    {
                        ans-=cnt[to[v]];
                        to[v]--;
                    }
                }
                v=pre[v];
            }
            printf("%d ",ans);
        }
    }

    return 0;
}
posted @ 2018-08-22 19:58  Maxwei_wzj  阅读(173)  评论(0编辑  收藏  举报