• CF 375D. Tree and Queries加强版!!!【dfs序分块 大小分类讨论】


    传送门

    题意:

    一棵树,询问一个子树内出现次数$ge k$的颜色有几种,Candy?这个沙茶自带强制在线


    吐槽:

    本来一道可以离散的莫队我非要强制在线用分块做;上午就开始写了然后发现思路错了...;改 下午继续写....然后发现看大了数据范围卡空间了...;改 然后又发现好多bug...;再改 然后发现TLE了... ;改块的大小....可恶又卡空间了.... ;改short...可恶溢出了;改unsigned short....可恶n总共才1e5怎么练unsigned short也溢出了.....; 开O2...还不行....;然后发现之前把块的大小和数量搞反了....;继续改块的大小再加上有理有据对本题特性的vector优化.....终于A了.................

    题解:

    一开始想成已经知道k预处理f不用第三维了(md那还用分块干什么)

    对出现次数$>S$和$le S$的分开讨论

    预处理$f[i][j][k]$为块i到块j出现次数$[k,S]$的有几种

    $s[i][j]$为前i块颜色j出现了几次

    询问的时候

    两边不完整的块暴力枚举

    $>S$的部分不超过$frac{N}{S}$种,单独暴力枚举(注意如果两边枚举过了就不能重复枚举了)

    $[k,S]$的部分直接用预处理的f

    #pragma GCC optimize ("O2")
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    #include <vector>
    using namespace std;
    typedef long long ll;
    const int N=1e5+5, M=245, S=425;
    inline int read(){
        char c=getchar();int x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    int n,Q,col,a[N],u,v,k;
    int cou[N], big[N], tot, mark[N];bool biiig[N];
    struct edge{int v,ne;}e[N<<1];
    int cnt,h[N];
    inline void ins(int u,int v){
        e[++cnt]=(edge){v,h[u]}; h[u]=cnt;
        e[++cnt]=(edge){u,h[v]}; h[v]=cnt;
    }
    int dfc,L[N],R[N];
    int t[N];
    void dfs(int u,int fa){
        L[u]=++dfc; a[dfc]=t[u];
        for(int i=h[u];i;i=e[i].ne)
            if(e[i].v!=fa) dfs(e[i].v, u);
        R[u]=dfc;
    }
    
    int block,m,pos[N];
    struct _blo{int l,r;}b[M];
    void ini(){
        //block=sqrt(n); 
        block=420;
        m=(n-1)/block+1;
        for(int i=1;i<=n;i++) pos[i]=(i-1)/block+1;
        for(int i=1;i<=m;i++) b[i].l=(i-1)*block+1, b[i].r=i*block;
        b[m].r=n;
    }
    
    struct Block{
        int f[M][M][S], c[N], s[M][N];
    
        void Set0(int x){
            for(int i=1;i<=col;i++) s[x][i]=s[x-1][i];
            for(int i=b[x].l; i<=b[x].r; i++) s[x][a[i]]++;
        }
    
        void Set1(int x){
            for(int t=x;t<=m;t++){
                for(int i=b[t].l; i<=b[t].r; i++) if(!biiig[ a[i] ]) c[a[i]]++;
                for(int i=b[t].l; i<=b[t].r; i++) if(!biiig[ a[i] ] && c[a[i]]>0){ 
                    int _=s[t-1][a[i]] - s[x-1][a[i]];
                    f[x][t][ _+c[a[i]] ]++;
                    f[x][t][ _ ]--;
                    c[a[i]]=0;
                }
                for(int i=block; i>=1; i--) f[x][t][i]+=f[x][t][i+1];
                for(int i=1; i<=block; i++) f[x][t][i]+=f[x][t-1][i];
            }
        }
    
        int Que(int l,int r,int k){ 
            int pl=pos[l], pr=pos[r];
            int ans=0;
            if(pl==pr){
                for(int i=l; i<=r; i++) c[a[i]]++;
                for(int i=l; i<=r; i++) if(c[a[i]]>0) ans+= c[a[i]]>=k, c[a[i]]=0;
            }else{
                for(int i=1; i<=tot; i++) mark[ big[i] ]=0;
                vector<int> v;
                int *rr=s[pr], *ll=s[pl-1];
                for(int i=l; i<=b[pl].r; i++){ 
                    mark[ a[i] ]=1;
                    if(rr[a[i]] - ll[a[i]]>=k)
                        c[a[i]]++, v.push_back(a[i]); 
                }
                for(int i=b[pr].l; i<=r; i++){
                    mark[ a[i] ]=1;
                    if(rr[a[i]] - ll[a[i]]>=k)
                        c[a[i]]++, v.push_back(a[i]); 
                }
    
                for(int i=0; i<(int)v.size(); i++) if(c[v[i]]>0){
                    int _=s[pr-1][v[i]] - s[pl][v[i]];
                    if(biiig[ v[i] ]) ans+= _+c[v[i]]>=k;
                    else ans+= (_<k && _+c[v[i]]>=k);
                    c[v[i]]=0;
                }
    
                if(k<=block) ans+=f[pl+1][pr-1][k]; 
                for(int i=1;i<=tot;i++) if(!mark[ big[i] ])
                    ans+= s[pr-1][big[i]] - s[pl][big[i]] >= k;
            }
            return ans;
        }
    }B;
    
    int main(){
    //    freopen("in","r",stdin);
        n=read(); Q=read(); ini();
        for(int i=1;i<=n;i++) a[i]=t[i]=read(), col=max(col, a[i]), cou[a[i]]++;
        for(int i=1;i<n;i++) ins(read(), read());
        dfs(1,0);
    
        for(int i=1;i<=col;i++) if(cou[i]>block) big[++tot]=i, biiig[i]=1;
        for(int i=1;i<=m;i++) B.Set0(i);
        for(int i=1;i<=m;i++) B.Set1(i);
    
        while(Q--){
            u=read(); k=read();
            printf("%d
    ", B.Que(L[u], R[u], k) );
        }
    }
  • 相关阅读:
    最小生成树
    BZOJ3894:文理分科(最大流)(同BZoj3438)
    BZOJ3438:小M的作物 (最大闭合权图->最小割)
    BZOJ 1305:dance跳舞(二分+最大流)
    BZOJ1266:上学路线route (最短路+最小割)
    BZOJ1854:游戏(二分图匹配)
    【PowerOJ1738】最小路径覆盖
    【SPOJ839】Optimal Marks 网络流
    【USACO】AC自动机
    【国家集训队2011】聪聪可可 树分治
  • 原文地址:https://www.cnblogs.com/candy99/p/6567213.html
Copyright © 2020-2023  润新知