zoukankan      html  css  js  c++  java
  • NOIP 模拟 6 模板

    题目

    题解

    这道题是一道启发式合并的题目,每次合并完重构一下线段树就可以,不用线段树合并。

    以操作时间为下标,建立一颗线段树,维护小球的个数与小球的颜色数,最后线段树上二分查找。

    我们先不用考虑每个节点放小球数的限制,最后二分查找时,找小球数 (leq) 限制数的所对应的节点下标的颜色数。

    在本题中,我们要用一个 (vector) 来存储,通过 (vector) 的动态内存释放来防止 (MLE)

    而且也要注意,在本题中颜色编号可能为负数,所以我们要先将其映射到正整数范围内,这个可以用 (map)

    还有一个挺玄学的地方就是启发式合并,通俗得说就是由一个小集合向一个大集合合并,这样可以减少合并次数,从而减小复杂度。

    这体现到本题上就是对于一个节点,如果其非叶节点,找到一个操作数最多的子树,将其它子树向这个重儿子合并。

    细节挺多,且卡常的地方也很多,具体看代码

    (ACkern 0.5emCODE:)

    Code
    #include<bits/stdc++.h>
    #define ri register int
    #define p(i) ++i
    using namespace std;
    namespace IO{
        char buf[1<<21],*p1=buf,*p2=buf;
        #define gc() p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++
        inline int read() {
            ri x=0,f=1;char ch=gc();
            while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
            while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
            return x*f; 
        }
    }
    using IO::read;//快读
    namespace nanfeng{
        #define cmax(x,y) ((x)>(y)?(x):(y))
        #define cmin(x,y) ((x)>(y)?(y):(x))
        #define FI FILE *IN
        #define FO FILE *OUT
        #define pb(x) push_back(x)
        #define node(c,id) (node){c,id} 
        const int N=1e5+7;
        struct edge{int v,nxt;}e[N<<1];
        struct node{int c,id;};
        int first[N],buc[N],tms[N],son[N],ans[N],ocu[N],t=1,tot,n,m,Q;
        map<int,int> col;//可能会有负数
        vector<node> ch[N];//二位数组会mle,node 可以用pair替换
        inline void add(int u,int v) {
            e[t].v=v;
            e[t].nxt=first[u];
            first[u]=t++;
        } 
        struct Seg{//封装
            #define ls(x) (x<<1)
            #define rs(x) (x<<1|1)
            struct Segmenttree{int sz,szc,lz;}T[N<<2];
            inline void init(int x) {
                T[1].sz=T[1].sz=0;
                T[1].lz=1;//懒标记实在初始化时用,类似与区间更新,没用到的区间就不初始化
                for (ri i(0);i<ch[x].size();p(i)) ocu[ch[x][i].c]=0;
            }
            inline void down(int x) {
                if (!T[x].lz) return;
                int l=ls(x),r=rs(x);
                T[x].lz=T[l].sz=T[l].szc=T[r].sz=T[r].szc=0;
                T[l].lz=T[r].lz=1;
            }
            inline void up(int x) {
                int l=ls(x),r=rs(x);
                T[x].sz=T[l].sz+T[r].sz;
                T[x].szc=T[l].szc+T[r].szc;
            }
            void update(int x,int l,int r,int k,int c,int nm) {
                if (l==r) {T[x].sz+=nm;T[x].szc+=c;return;}
                down(x);
                int mid=(l+r)>>1;
                if (k<=mid) update(ls(x),l,mid,k,c,nm);
                else update(rs(x),mid+1,r,k,c,nm);
                up(x);
            }
            inline void merge(int x) {
                for (ri i(0);i<ch[x].size();p(i)) {
                    node tmp=ch[x][i];
                    if (!ocu[tmp.c]) {
                        update(1,1,m,tmp.id,1,1);
                        ocu[tmp.c]=tmp.id;
                    } else if (ocu[tmp.c]>tmp.id) {
                        update(1,1,m,ocu[tmp.c],-1,0);
                        update(1,1,m,tmp.id,1,1);
                        ocu[tmp.c]=tmp.id;
                    } else update(1,1,m,tmp.id,0,1);
                }
            }
            int query(int x,int l,int r,int k) {
                if (!k) return 0;
                if (l==r) return T[x].szc;
                int mid=(l+r)>>1;
                down(x);
                int lt=ls(x),rt=rs(x);
                if (T[lt].sz<=k) return T[lt].szc+query(rt,mid+1,r,k-T[lt].sz);//以小球数二分
                else return query(lt,l,mid,k);
            }
        }T;
        void dfs_init(int x,int fa) {
            tms[x]=ch[x].size();
            for (ri i(first[x]),v;i;i=e[i].nxt) {
                if ((v=e[i].v)==fa) continue;
                dfs_init(v,x);
                tms[x]+=tms[v];
                if (tms[v]>tms[son[x]]) son[x]=v;//这里有一个优化,因为是启发式合并,所以要先处理出操作数多的重儿子,由小向大合并,减少合并数
            }
        }
        void dfs(int x,int fa) {
            for (ri i(first[x]),v;i;i=e[i].nxt) {
                if ((v=e[i].v)==fa||v==son[x]) continue;
                dfs(v,x);T.init(v);//记得回溯
            }
            if (son[x]) dfs(son[x],x);//重儿子单独搜,不用回溯
            T.merge(x);
            for (ri i(first[x]),v;i;i=e[i].nxt) {
                if ((v=e[i].v)==fa||v==son[x]) continue;
                T.merge(v);
            }
            ans[x]=T.query(1,1,m,buc[x]);//二分查找
            if (son[x]) {
                int sx=son[x];
                for (ri i(0);i<ch[x].size();p(i)) ch[sx].pb(ch[x][i]);
                swap(ch[x],ch[sx]);//启发式合并
                for (ri i(first[x]),v;i;i=e[i].nxt) {
                    if ((v=e[i].v)==fa||v==son[x]) continue;
                    for (ri i(0);i<ch[v].size();p(i)) ch[x].pb(ch[v][i]);
                }
            }
        }
        inline int main() {
            // FI=freopen("nanfeng.in","r",stdin);
            // FO=freopen("nanfeng.out","w",stdout);
            n=read();
            for (ri i(1);i<n;p(i)) {
                int u=read(),v=read();
                add(u,v);add(v,u);
            }
            for (ri i(1);i<=n;p(i)) buc[i]=read();
            m=read();
            for (ri i(1);i<=m;p(i)) {
                int x=read(),c=read();
                if (!col[c]) col[c]=p(tot),c=tot;//映射成整数范围
                else c=col[c];
                ch[x].pb(node(c,i));
            }
            dfs_init(1,0);dfs(1,0);
            Q=read();
            for (ri i(1),x;i<=Q;p(i)) printf("%d
    ",ans[x=read()]); 
            return 0;
        }
    }
    int main() {return nanfeng::main();}
    

    时间复杂度为 (mathcal O(nlog^2n))?我不太会分析,哪位大佬教我一下?

  • 相关阅读:
    Python时钟,计算程序运行时间
    关于等高线绘制和全平面坐标节点生成
    Springboot配置文件映射
    Docker和Rancher
    ElasticSearch story(二)
    Elastic Story(一)
    由数量众多照片拼贴而成的马赛克图片
    lnmp一键安装包配置laravel项目
    mysql 创建用户与授权、修改密码
    centos 安装 ntpdate 并同步时间
  • 原文地址:https://www.cnblogs.com/nanfeng-blog/p/14876336.html
Copyright © 2011-2022 走看看