zoukankan      html  css  js  c++  java
  • 【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;
    }
  • 相关阅读:
    PHP用户注册邮箱验证激活帐号
    利用openssl进行RSA加密解密
    RSA算法使用介绍
    JS七种加密解密方法
    JS调用PHP 和 PHP调用JS的方法举例
    php注册登录时生成的验证码
    Joomla!网站扫描工具joomscan
    Xamarin XAML语言教程控件模板的模板绑定
    Xamarin.Forms使用Slider注意问题
    ASP.net 资源请求漏洞利用工具PadBuster
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793278.html
Copyright © 2011-2022 走看看