zoukankan      html  css  js  c++  java
  • 「LibreOJ NOI Round #1」验题

     麻烦的动态DP写了2天

    简化题意:给树,求比给定独立集字典序大k的独立集是哪一个

    主要思路:

    k排名都是类似二分的按位确定过程。

    字典序比较本质是LCP下一位,故枚举LCP,看多出来了多少个独立集,然后判断;枚举完LCP,再往回做过去。

    外面:

    假如是一串0101010表示每个点有没有在独立集里,然后比较这个01串的字典序?

    枚举LCP,LCP下一位:原来是0,这次就是1;原来是1,这次必须还是1。后面的随便选择。找方案数

    但是这里是一般的字典序,两个1中间的0都会省略,使得大小比较没有那么容易

    但是也有字典序按位比较的方法(2随便选)

    写成01序列00001010100001001010000

    首先,最后的0000先都变成2222,找方案数,(因为后面再放上一个1,由于长度更长,所以字典序还是大的。如果原来序列后面有1就不行了(也是为什么下面要跳过中间的0))

    不够,每次找最后一个1,变成0,后面都是2,找方案数

    两个1中间的0都变成2,这些0变成1的话,直接就比原来的字典序小了。

    注意,每一次会有一个后面全是00000的,特殊-1 

    最后如果剩下lcp是0,就S=∅ return 0

    否则往回找,开始从(1,0)或者(1,1)后面第一个(0,0)开始找(后面的暂时还是2)

    大概就是两种情况都是从红色箭头开始

    先填1,(因为1小),如果方案大于等于remain,就把1放这儿,remain-=1(因为后面都填0也是一种方案了)

    如果小于remain,减掉这次的方案数,然后放0

    当途中remain==0,一定是k从1到0,直接break掉

    最后in[x]=1的就是最终独立集了。

    动态DP:

    统计独立集方案数,f[x][0],f[x][1]表示方案数即可

    方案数大于k,和k+2取min

    取min了,修改轻儿子不能直接把轻儿子贡献除去,线段树维护轻儿子的f[x][0]+f[x][1]和f[x][0]

    每个重链开一个线段树

    注意:
    1.重链在top建立,直接不断找重儿子即可

    2.判乘法>k,会爆long long,用long double会被卡常,所以

    return __builtin_clzll(a)+__builtin_clzll(b)<66?k+2:a*b;

    3.常数优化还可以用const mat &t=...直接引用减少复制常数。

    代码:

    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define mid ((l+r)>>1)
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;x=0;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=1e6+6;
    const int M=4e6+5;
    int n;
    int b[N][2];
    int a[N],I;
    int ori[N];
    ll k;
    int hd[N],cnt;
    int bian[N][2];
    struct node{
        int nxt,to;
    }e[2*N];
    void addedge(int x,int y){
        e[++cnt].nxt=hd[x];
        e[cnt].to=y;
        hd[x]=cnt;
    }
    ll mul(const ll &a,const ll &b){
        if(!a||!b) return 0;
        if(a>k+1||b>k+1) return k+2;
        return __builtin_clzll(a)+__builtin_clzll(b)<66?k+2:a*b;
    }
    ll add(const ll &a,const ll &b){
        return a+b>k+1?k+2:a+b;
    }
    struct mat{
        ll a[2][2];
        void clear(){memset(a,0,sizeof a);}
        void pre(){a[0][0]=1;a[1][1]=1;a[0][1]=a[1][0]=0;}
        void init(){a[0][0]=a[1][0]=a[0][1]=1;a[1][1]=0;}
        mat operator *(const mat&b){
            mat c;
            c.a[0][0]=add(mul(a[0][0],b.a[0][0]),mul(a[0][1],b.a[1][0]));
            c.a[0][1]=add(mul(a[0][0],b.a[0][1]),mul(a[0][1],b.a[1][1]));
            c.a[1][0]=add(mul(a[1][0],b.a[0][0]),mul(a[1][1],b.a[1][0]));
            c.a[1][1]=add(mul(a[1][0],b.a[0][1]),mul(a[1][1],b.a[1][1]));
            return c;
        }
        void op(){
            cout<<a[0][0]<<" "<<a[0][1]<<endl;
            cout<<a[1][0]<<" "<<a[1][1]<<endl;
        }
    }st[N],base,nowdp;
    
    int dfn[N],top[N],dep[N],son[N],sz[N],fa[N];
    int fdfn[N];
    int low[N];
    int df;
    int totson[N];
    int num[N];//i is i's fa's num[i] son
    int in[N];//0 not 1 yes
    ll f[N][2];
    
    namespace seg1{//get lian
    int tot;
    int ls[M],rs[M];
    mat data[M];
    struct tr1{
        int ss,nd;
        int rt;
        void pushup(int x){
            data[x]=data[rs[x]]*data[ls[x]];
        }
        mat get(){
            return data[rt];
        }
        void build(int &x,int l,int r){
            x=++tot;
        //    cout<<" x "<<x<<" "<<l<<" "<<r<<endl;
            if(l==r){
                data[x]=st[fdfn[l]];return;
            }
            build(ls[x],l,mid);build(rs[x],mid+1,r);
            pushup(x);
        }
        void chan(int p,ll c0,ll c1){
            upda(rt,ss,nd,p,c0,c1);
        }
        mat que(int L,int R){
            return query(rt,ss,nd,L,R);
        }
        mat query(int x,int l,int r,int L,int R){
            if(L<=l&&r<=R){
                return data[x];
            }
            mat ret;
            ret.pre();
            if(mid<R) ret=ret*query(rs[x],mid+1,r,L,R);
            if(L<=mid) ret=ret*query(ls[x],l,mid,L,R);
            return ret;
        }
        void upda(int x,int l,int r,int p,ll c0,ll c1){
            if(l==r){
                data[x].a[0][0]=c0;
                data[x].a[1][0]=c0;
                data[x].a[0][1]=c1;
                return;
            }
            if(p<=mid) upda(ls[x],l,mid,p,c0,c1);
            else upda(rs[x],mid+1,r,p,c0,c1);
            pushup(x);
        }
    };
    
    }using seg1::tr1;
    tr1 t1[N];
    
    int mem[N];
    namespace seg2{//get son
    int tot;
    int ls[M],rs[M];
    ll s0[M],s1[M];
    struct tr2{
        int sz,rt;
        void pushup(int x){
            s0[x]=mul(s0[ls[x]],s0[rs[x]]);
            s1[x]=mul(s1[ls[x]],s1[rs[x]]);
        }
        void build(int &x,int l,int r){
            x=++tot;
            if(l==r){
                s0[x]=f[mem[l]][0]+f[mem[l]][1];
                s1[x]=f[mem[l]][0];
                return;
            }
            build(ls[x],l,mid);build(rs[x],mid+1,r);
            pushup(x);
        }
        void chan(int p,ll c0,ll c1){
            upda(rt,1,sz,p,c0,c1);
        }
        void upda(int x,int l,int r,int p,ll c0,ll c1){
            if(l==r){
                s0[x]=c0;
                s1[x]=c1;
                return;
            }
            if(p<=mid) upda(ls[x],l,mid,p,c0,c1);
            else upda(rs[x],mid+1,r,p,c0,c1);
            pushup(x);
        }
        void spe(int &x){
            x=++tot;
            s0[x]=1;s1[x]=1;
        }
        ll get0(){
            return s0[rt];
        }
        ll get1(){
            return s1[rt];
        }
    };
    
    }using seg2::tr2;
    tr2 t2[N];
    
    int sta[N],tp;
    void dfs1(int x,int d){
        dep[x]=d;
        sz[x]=1;
        if(!in[x])f[x][0]=1;
        else f[x][1]=1;
        for(reg i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(y==fa[x]) continue;
            fa[y]=x;
            dfs1(y,x);
            sz[x]+=sz[y];
            if(sz[y]>sz[son[x]]) son[x]=y;
        }
    }
    void dfs2(int x){
        dfn[x]=++df;
        sta[++tp]=x;
        fdfn[df]=x;
        if(!top[x]) top[x]=x;
        if(son[x]) top[son[x]]=top[x],++totson[x],dfs2(son[x]);
        st[x].init();
        
        for(reg i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(y==son[x]) continue;
            if(y==fa[x]) continue;
            num[y]=totson[x];
            ++totson[x];
            dfs2(y);
            if(in[x]){
                st[x].a[0][1]*=(f[y][0]);
            }else{
                st[x].a[0][0]*=(f[y][0]+f[y][1]);
                st[x].a[1][0]*=(f[y][0]+f[y][1]);
            }
        }
        if(in[x]) st[x].a[0][0]=st[x].a[1][0]=0;
        else st[x].a[0][1]=0;
        
        if(top[x]==x){    
            t1[x].ss=dfn[x];
            t1[x].nd=dfn[sta[tp]];
            t1[x].build(t1[x].rt,t1[x].ss,t1[x].nd);
            int z;
            do{
                z=sta[tp];
                --tp;
            }while(z!=x);
        }
        if(totson[x]<=1){//special 
            t2[x].spe(t2[x].rt);    
        }
        else if(totson[x]>1){
            int lp=0;
            for(reg i=hd[x];i;i=e[i].nxt){
                int y=e[i].to;
                if(y==fa[x]||y==son[x]) continue;
                mem[++lp]=y;
            }
            t2[x].sz=lp;
            t2[x].build(t2[x].rt,1,lp);
        }
    }
    mat upda(int x){
        mat ret;
        ret.clear();
        while(x){
            if(in[x]==0){//must not
                t1[top[x]].chan(dfn[x],t2[x].get0(),0);
            }else if(in[x]==1){//must yes
                t1[top[x]].chan(dfn[x],0,t2[x].get1());
            }else{//either
                t1[top[x]].chan(dfn[x],t2[x].get0(),t2[x].get1());
            }
            ret=base*t1[top[x]].get();
            x=top[x];
            if(fa[x]){
                t2[fa[x]].chan(num[x],add(ret.a[0][0],ret.a[0][1]),ret.a[0][0]);
            }
            x=fa[x];
        }
        return ret;
    }
    int main(){
        rd(n);
        scanf("%lld",&k);
        base.a[0][0]=1;
        
        for(reg i=1;i<n;++i) rd(b[i][0]),++b[i][0];
        for(reg i=1;i<n;++i) {
            rd(b[i][1]);
            ++b[i][1];
            addedge(b[i][0],b[i][1]);
            addedge(b[i][1],b[i][0]);
        }
        rd(I);
        for(reg i=1;i<=I;++i) rd(a[i]),++a[i],in[a[i]]=1,ori[a[i]]=1;
        sort(a+1,a+I+1);
        
        if(k==0){
            for(reg i=1;i<=I;++i){
                printf("%d ",a[i]-1);
            }
            return 0;
        }
        dfs1(1,1);
        dfs2(1);
        //shu lian pou fen 
        
        int lcp=I;//warning!! numth
        ll remain=k;
        for(reg i=a[lcp]+1;i<=n;++i){
            in[i]=2;
            nowdp=upda(i);
        }
        ll tot=add(nowdp.a[0][0],nowdp.a[0][1]);
        int lp=0;
        
        if(tot-1<remain){
            remain-=max(1LL*0,tot-1);
            int ptr=a[lcp]-1;
            for(;lcp;--lcp){
                in[a[lcp]]=0;
                nowdp=upda(a[lcp]);
                tot=add(nowdp.a[0][0],nowdp.a[0][1]);
                if(tot-1<remain){
                    remain-=tot-1;
                    in[a[lcp]]=2;
                    nowdp=upda(a[lcp]);
                }else{
                    break;
                }
                ptr=a[lcp]-1;
                while(ptr&&ori[ptr]!=1){
                    in[ptr]=2;
                    nowdp=upda(ptr);
                    --ptr;
                }
            }
            if(!lcp){
                return 0;
            }
        }
        
        lp=a[lcp]+1;
        for(reg i=lp;i<=n;++i){
            in[i]=1;
            nowdp=upda(i);
            tot=add(nowdp.a[0][0],nowdp.a[0][1]);
            if(tot<remain){
                remain-=tot;
                in[i]=0;
                nowdp=upda(i);
            }else{
                --remain;
            }
            if(remain==0) break;
        }
        for(reg i=1;i<=n;++i){
            if(in[i]==1){
                printf("%d ",i-1);
            }
        }
        return 0;
    }
    
    }
    signed main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2019/2/20 11:04:52
    */
    
    
     
     

    字典序处理、轻儿子维护值得注意

  • 相关阅读:
    c++ 设计模式3 (重构技法 Template Method)
    C++ 设计模式2 (面向对象设计原则)
    c++ 设计模式1
    算法总结—二分搜索与旋转排序数组
    c++ 构造函数,拷贝构造函数,析构函数与赋值操作符
    题解 P2330 【[SCOI2005]繁忙的都市】
    题解 CF896C 【Willem, Chtholly and Seniorious】
    题解 P3369 【【模板】普通平衡树】
    题解 CF383C 【Propagating tree】
    题解 P1179 【数字统计】
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10419869.html
Copyright © 2011-2022 走看看