「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
*/


 
 

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

posted @ 2019-02-22 17:57  *Miracle*  阅读(587)  评论(0编辑  收藏  举报