Loading

【考后总结】7 月 NOI 模拟赛 1

Page Views Count

7.2 冲刺国赛自测 9

T1 字符串

一个合法位置 \([l,r]\) 代表 \([1,x]\)\([l,l+x-1]\) 相同,\([y,n]\)\([r-y+1,r]\) 相同,类似 \(x\in \mathrm{Border}(l+x-1)\)

对正反串做 KMP,建失配树,类似要求 \(x\) 子树和 \(y\) 子树的交,而 \((l+x-1)+1=(r-y+1)\) 所以正串失配树子树里节点 \(u\) 对应反串失配树子树里节点 \(n-u\),二维数点即可。

点击查看代码
int rt[maxn];
struct SegmentTree{
#define mid ((l+r)>>1)
    int ch[maxn*40][2],tot;
    int siz[maxn*40];
    inline void clear(){
        tot=0;
        memset(ch,0,sizeof(ch));
        memset(siz,0,sizeof(siz));
    }
    inline void new_node(int &pos){
        ch[++tot][0]=ch[pos][0],ch[tot][1]=ch[pos][1],siz[tot]=siz[pos];
        pos=tot;
    }
    void insert(int &pos,int l,int r,int p){
        new_node(pos);
        ++siz[pos];
        if(l==r) return;
        if(p<=mid) insert(ch[pos][0],l,mid,p);
        else insert(ch[pos][1],mid+1,r,p);
    }
    int query(int lpos,int rpos,int l,int r,int pl,int pr){
        if(pl<=l&&r<=pr) return siz[rpos]-siz[lpos];
        int res=0;
        if(pl<=mid) res+=query(ch[lpos][0],ch[rpos][0],l,mid,pl,pr);
        if(pr>mid) res+=query(ch[lpos][1],ch[rpos][1],mid+1,r,pl,pr);
        return res;
    }
#undef mid
}S;

int t;
int n,q;
char s1[maxn],s2[maxn];
int nxt1[maxn],nxt2[maxn];
vector<int> E1[maxn],E2[maxn];
int fa1[maxn],siz1[maxn],dfn1[maxn],dfncnt1;
int fa2[maxn],siz2[maxn],dfn2[maxn],dfncnt2;
int id[maxn];
inline void get_nxt(){
    nxt1[1]=0;
    E1[0].push_back(1);
    for(int i=2,j=0;i<=n;++i){
        while(j&&s1[i]!=s1[j+1]) j=nxt1[j];
        if(s1[i]==s1[j+1]) ++j;
        nxt1[i]=j;
        E1[j].push_back(i);
    }
    nxt2[1]=0;
    E2[0].push_back(1);
    for(int i=2,j=0;i<=n;++i){
        while(j&&s2[i]!=s2[j+1]) j=nxt2[j];
        if(s2[i]==s2[j+1]) ++j;
        nxt2[i]=j;
        E2[j].push_back(i);
    }
}
void dfs1(int u,int f){
    fa1[u]=f,siz1[u]=1;
    if(u) dfn1[u]=++dfncnt1,id[dfn1[u]]=u;
    for(int v:E1[u]){
        if(v==f) continue;
        dfs1(v,u);
        siz1[u]+=siz1[v];
    }
}
void dfs2(int u,int f){
    fa2[u]=f,siz2[u]=1;
    if(u) dfn2[u]=++dfncnt2;
    for(int v:E2[u]){
        if(v==f) continue;
        dfs2(v,u);
        siz2[u]+=siz2[v];
    }
}

int main(){
    freopen("string.in","r",stdin);
    freopen("string.out","w",stdout);
    t=read();
    while(t--){
        n=read(),q=read();
        for(int i=0;i<=n;++i){
            nxt1[i]=nxt2[i]=0;
            E1[i].clear(),E2[i].clear();
            dfncnt1=dfncnt2=0;
            fa1[i]=siz1[i]=dfn1[i]=fa2[i]=siz2[i]=dfn2[i]=0;
        }
        S.clear();
        scanf("%s",s1+1);
        for(int i=1;i<=n;++i) s2[i]=s1[n-i+1];
        get_nxt();
        dfs1(0,0);
        dfs2(0,0);
        rt[0]=0;
        for(int i=1;i<=n;++i){
            int j=dfn2[n-id[i]];
            rt[i]=rt[i-1];
            if(j) S.insert(rt[i],1,n,j);
        }
        while(q--){
            int x=read(),y=read();
            printf("%d\n",S.query(rt[dfn1[x]-1],rt[dfn1[x]+siz1[x]-1],1,n,dfn2[y],dfn2[y]+siz2[y]-1));
        }
    }
    return 0;
}

T2 计树

魔幻转移是枚举 \(x\in [1,n)\),令 \(p_i=[p_i>x]\),求所有 \(p\) 中相邻位置不同的个数和就是答案,正确性在于 \(p_i=a,p_{i+1}=b\ (a<b)\) 时,\(p_i\neq p_{i+1}\) 恰好为 \(x\in [a,b)\),贡献就是 \(b-a\)

考虑 DP,目标是子树内构成 \(p\) 的子序列中某两个相邻不同的方案数,同时需要维护出每个位置上 \(0/1\) 的方案数,也需要维护出子树内方案总数。

\(f_u\)\(u\) 子树内合法 \(p\) 序列方案数,\(g_{u,k,0/1}\)\(u\) 子树内 \(p_k=0/1\) 的方案数,\(h_{u,k}\)\(u\) 子树内 \(p_k\neq p_{k+1}\) 的方案数。

\(f\) 转移不断乘 \(f_v\) 和组合数即可。

\(g\) 转移讨论 \(k\) 来自已经合并过的子树或是 \(v\),其余位置的排布方案乘组合数。

\(h\) 讨论同理,更复杂一点需要讨论 \(k\)\(k+1\) 分别来自哪里,方案依旧是组合数。

转移精细实现复杂度是树上背包 \(O(n^2)\)

根据题意,\(p\) 序列第一个元素一定是 \(u\),所以 \(p_1\) 实际根据 \(x\) 固定,最后把 \(u\) 加进去时先向右平移,再算 \(1\) 处值即可。

点击查看代码
inline int q_pow(int A,int B,int P){
    int res=1;
    while(B){
        if(B&1) res=1ll*res*A%P;
        A=1ll*A*A%P;
        B>>=1;
    }
    return res;
}

int n,rt;
vector<int> E[maxn];

int fact[maxn],inv_fact[maxn];
inline int C(int N,int M){
    if(N<M) return 0;
    return 1ll*fact[N]*inv_fact[M]%mod*inv_fact[N-M]%mod;
}

int siz[maxn];
int f[maxn],g[maxn][maxn][2],h[maxn][maxn],tmpg[maxn][2],tmph[maxn];
void dfs(int u,int fa,int x){
    f[u]=1;
    for(int v:E[u]){
        if(v==fa) continue;
        dfs(v,u,x);
        for(int i=1;i<=siz[u]+siz[v];++i) tmpg[i][0]=tmpg[i][1]=0,tmph[i]=0;
        for(int i=0;i<=siz[u];++i){
            for(int j=0;j<=siz[v];++j){
                if(i){
                    tmpg[i+j][0]=(tmpg[i+j][0]+1ll*g[u][i][0]*f[v]%mod*C(i+j-1,j)%mod*C(siz[u]+siz[v]-(i+j),siz[v]-j)%mod)%mod;
                    tmpg[i+j][1]=(tmpg[i+j][1]+1ll*g[u][i][1]*f[v]%mod*C(i+j-1,j)%mod*C(siz[u]+siz[v]-(i+j),siz[v]-j)%mod)%mod;
                }
                if(j){
                    tmpg[i+j][0]=(tmpg[i+j][0]+1ll*f[u]*g[v][j][0]%mod*C(i+j-1,j-1)%mod*C(siz[u]+siz[v]-(i+j),siz[v]-j)%mod)%mod;
                    tmpg[i+j][1]=(tmpg[i+j][1]+1ll*f[u]*g[v][j][1]%mod*C(i+j-1,j-1)%mod*C(siz[u]+siz[v]-(i+j),siz[v]-j)%mod)%mod;
                }
            }
        }
        for(int i=0;i<=siz[u];++i){
            for(int j=0;j<=siz[v];++j){
                if(i&&i<siz[u]){
                    tmph[i+j]=(tmph[i+j]+1ll*h[u][i]*f[v]%mod*C(i+j-1,j)%mod*C(siz[u]+siz[v]-(i+j+1),siz[v]-j)%mod)%mod;
                }
                if(j&&j<siz[v]){
                    tmph[i+j]=(tmph[i+j]+1ll*f[u]*h[v][j]%mod*C(i+j-1,j-1)%mod*C(siz[u]+siz[v]-(i+j+1),siz[v]-(j+1))%mod)%mod;
                }
                if(i&&j<siz[v]){
                    tmph[i+j]=(tmph[i+j]+1ll*g[u][i][0]*g[v][j+1][1]%mod*C(i+j-1,j)%mod*C(siz[u]+siz[v]-(i+j+1),siz[v]-(j+1))%mod)%mod;
                    tmph[i+j]=(tmph[i+j]+1ll*g[u][i][1]*g[v][j+1][0]%mod*C(i+j-1,j)%mod*C(siz[u]+siz[v]-(i+j+1),siz[v]-(j+1))%mod)%mod;
                }
                if(i<siz[u]&&j){
                    tmph[i+j]=(tmph[i+j]+1ll*g[u][i+1][0]*g[v][j][1]%mod*C(i+j-1,j-1)%mod*C(siz[u]+siz[v]-(i+j+1),siz[v]-j)%mod)%mod;
                    tmph[i+j]=(tmph[i+j]+1ll*g[u][i+1][1]*g[v][j][0]%mod*C(i+j-1,j-1)%mod*C(siz[u]+siz[v]-(i+j+1),siz[v]-j)%mod)%mod;
                }
            }
        }
        f[u]=1ll*f[u]*f[v]%mod*C(siz[u]+siz[v],siz[v])%mod;
        siz[u]+=siz[v];
        for(int i=1;i<=siz[u];++i) g[u][i][0]=tmpg[i][0],g[u][i][1]=tmpg[i][1],h[u][i]=tmph[i];
    }
    ++siz[u];
    for(int i=siz[u];i>1;--i) g[u][i][0]=g[u][i-1][0],g[u][i][1]=g[u][i-1][1],h[u][i]=h[u][i-1];
    int now=(u>x);
    g[u][1][0]=g[u][1][1]=h[u][1]=0;
    g[u][1][now]=f[u];
    h[u][1]=g[u][2][now^1];
}

int ans;

int main(){
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    n=read(),rt=read();
    fact[0]=1;
    for(int i=1;i<=n;++i) fact[i]=1ll*fact[i-1]*i%mod;
    inv_fact[0]=1,inv_fact[n]=q_pow(fact[n],mod-2,mod);
    for(int i=n-1;i>=1;--i) inv_fact[i]=1ll*inv_fact[i+1]*(i+1)%mod;
    for(int i=1;i<n;++i){
        int u=read(),v=read();
        E[u].push_back(v);
        E[v].push_back(u);
    }
    for(int x=1;x<n;++x){
        memset(siz,0,sizeof(siz));
        memset(f,0,sizeof(f));
        memset(g,0,sizeof(g));
        memset(h,0,sizeof(h));
        dfs(rt,0,x);
        for(int i=1;i<n;++i) ans=(ans+h[rt][i])%mod;
    }
    printf("%d\n",ans);
    return 0;
}

T3 数论

不妨把每个 \(p_i\)\(a_j\) 全部拆开,改为 \(p_i\)\(a_j\) 的次数是 \(c_{i,j}\)\(\sum_{i,j} c_{i,j}=N\)

答案就可以写成:

\[\sum_{\sum_{i,j} c_{i,j}=N}\prod_{i=1}^n\prod_{j=1}^m [c_{i,j}=0]+[c_{i,j}>0](p_i-1)p_i^{c_{i,j}-1} \]

\(\sum_{i,j} c_{i,j}=N\) 容易想到生成函数,那么 \(F_i(x)\) 对应质数 \(p_i\),则:

\[F_i(x)=1+\sum_{k\ge 1} (p_i-1)p_i^{c_{i,j}-1} \]

求封闭形式,得到:

\[F_i(x)=\dfrac{1-x}{1-p_ix} \]

最终要求:

\[[x^N]\prod_{i=1}^n F_i^m(x)=[x^N]\left(\dfrac{\prod_{i=1}^n (1-x)}{\prod_{i=1}^n (1-p_ix)}\right)^m \]

可以分治乘+快速幂,最后得到一个形如 \([x^N]\dfrac{f(x)}{g(x)}\) 的结果,Bostan-Mori 算法解决,复杂度 \(O(n\log n\log N)\)

点击查看代码
inline int q_pow(int A,int B,int P){
    int res=1;
    while(B){
        if(B&1) res=1ll*res*A%P;
        A=1ll*A*A%P;
        B>>=1;
    }
    return res;
}

int rev[maxn];
int base,w[maxn];
struct Poly{
    const static int g=3;
    const static int inv_g=332748118;
    int deg;
    vector<ull> f;
    ull& operator[](const int &i){return f[i];}
    ull operator[](const int &i)const{return f[i];}
    inline void set(int L){deg=L;f.resize(L);}
    inline void clear(int L,int R){for(int i=L;i<=R;++i)f[i]=0;}
    inline void output(int L){for(int i=0;i<L;++i)printf("%llu ",f[i]);printf("\n");}
    inline void NTT(int L,bool type){
        set(L);
        for(int i=1;i<L;++i){
            rev[i]=(rev[i>>1]>>1)+(i&1?L>>1:0);
            if(i<rev[i]) swap(f[i],f[rev[i]]);
        }
        for(int d=1;d<L;d<<=1){
            base=q_pow(type?g:inv_g,(mod-1)/(2*d),mod);
            w[0]=1;
            for(int i=1;i<d;++i) w[i]=1ll*w[i-1]*base%mod;
            for(int i=0;i<L;i+=d<<1){
                for(int j=0;j<d;++j){
                    ull x=f[i+j],y=f[i+d+j]*w[j]%mod;
                    f[i+j]=x+y,f[i+d+j]=x-y+mod;
                }
            }
        }
        for(int i=0;i<L;++i) f[i]%=mod;
        if(!type){
            int inv_L=q_pow(L,mod-2,mod);
            for(int i=0;i<L;++i) f[i]=f[i]*inv_L%mod;
        }
    }
    inline Poly Pow(int k){
        Poly res=(*this),base=(*this);
        --k;
        int L=deg;
        while(k){
            L<<=1;
            base.NTT(L,1);
            if(k&1){
                res.NTT(L,1);
                for(int i=0;i<L;++i) res[i]=res[i]*base[i]%mod;
                res.NTT(L,0);
            }
            for(int i=0;i<L;++i) base[i]=base[i]*base[i]%mod;
            base.NTT(L,0);
            k>>=1;
        }
        return res;
    }
};

int a[maxn];

Poly Conquer(int l,int r){
    if(l==r){
        Poly F;
        F.set(2);
        F[0]=1ull,F[1]=(ull)mod-a[l];
        return F;
    }
    int mid=(l+r)>>1;
    Poly F=Conquer(l,mid),G=Conquer(mid+1,r);
    int L=1;
    while(L<=(r-l+1)) L<<=1;
    Poly H;
    H.set(L);
    F.NTT(L,1),G.NTT(L,1);
    for(int i=0;i<L;++i) H[i]=F[i]*G[i]%mod;
    H.NTT(L,0);
    return H;
}

inline void Bostan_Mori(int N,Poly F,Poly G){
    int L=F.deg;
    Poly H,A,B;
    F.set(L<<1),G.set(L<<1),H.set(L<<1),A.set(L<<1),B.set(L<<1);
    while(N){
        H=G;
        for(int i=1;i<L;i+=2) H[i]=mod-H[i];
        F.NTT(L<<1,1),G.NTT(L<<1,1),H.NTT(L<<1,1);
        for(int i=0;i<L<<1;++i) A[i]=F[i]*H[i]%mod,B[i]=G[i]*H[i]%mod;
        A.NTT(L<<1,0),B.NTT(L<<1,0);
        for(int i=0;i<L;++i) A[i]=A[2*i+(N&1)];
        for(int i=0;i<L;++i) B[i]=B[2*i];
        A.clear(L,(L<<1)-1),B.clear(L,(L<<1)-1);
        F=A,G=B;
        N>>=1;
    }
    printf("%llu\n",F[0]*q_pow((int)G[0],mod-2,mod)%mod);
}

int N,n,m;
int p[maxn];

int main(){
    freopen("math.in","r",stdin);
    freopen("math.out","w",stdout);
    N=read(),n=read(),m=read();
    for(int i=1;i<=n;++i) p[i]=read();
    Poly F,G;
    for(int i=1;i<=n;++i) a[i]=1;
    F=Conquer(1,n);
    F=F.Pow(m);
    for(int i=1;i<=n;++i) a[i]=p[i];
    G=Conquer(1,n);
    G=G.Pow(m);
    Bostan_Mori(N,F,G);
    return 0;
}

7.6 冲刺国赛模拟 31

T1 标志

原题:Luogu-P3170 CQOI 2015 标识设计

赛时写的分类讨论,写挂一车。

可以 \(O(n^6)\) 枚举三个 L 的拐点位置。计算方案数考虑容斥,手动把式子拆开算。

点击查看代码
int n,m;
char s[maxn][maxn];
int sumr[maxn][maxn],sumc[maxn][maxn];

ll ans;

int main(){
    freopen("logo.in","r",stdin);
    freopen("logo.out","w",stdout);
    n=read(),m=read();
    for(int i=1;i<=n;++i){
        scanf("%s",s[i]+1);
    }
    for(int i=1;i<=n;++i){
        for(int j=m;j>=1;--j){
            if(s[i][j]=='.') sumr[i][j]=sumr[i][j+1]+1;
            else sumr[i][j]=0;
        }
    }
    for(int j=1;j<=m;++j){
        for(int i=1;i<=n;++i){
            if(s[i][j]=='.') sumc[i][j]=sumc[i-1][j]+1;
            else sumc[i][j]=0;
        }
    }
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            if(s[i][j]=='.') --sumr[i][j],--sumc[i][j];
        }
    }
    for(int x1=1;x1<=n;++x1){
        for(int y1=1;y1<=m;++y1){
            for(int x2=x1;x2<=n;++x2){
                for(int y2=1;y2<=m;++y2){
                    for(int x3=x2;x3<=n;++x3){
                        for(int y3=1;y3<=m;++y3){
                            if(s[x1][y1]=='#'||s[x2][y2]=='#'||s[x3][y3]=='#') continue;
                            if(!sumr[x1][y1]||!sumc[x1][y1]||!sumr[x2][y2]||!sumc[x2][y2]||!sumr[x3][y3]||!sumc[x3][y3]) continue;
                            if(x1==x2&&y1>=y2) continue;
                            if(x1==x3&&y1>=y3) continue;
                            if(x2==x3&&y2>=y3) continue;
                            ll now;
                            int r1=sumr[x1][y1],c1=sumc[x1][y1],r2=sumr[x2][y2],c2=sumc[x2][y2],r3=sumr[x3][y3],c3=sumc[x3][y3];
                            if(x1==x2&&x2==x3) now=1ll*min(r1,y2-y1-1)*c1*min(r2,y3-y2-1)*c2*r3*c3;
                            else if(x1==x2){
                                if(y3==y1) now=1ll*min(r1,y2-y1-1)*c1*r2*c2*r3*min(c3,x3-x1-1);
                                else if(y3==y2) now=1ll*min(r1,y2-y1-1)*c1*r2*c2*r3*min(c3,x3-x2-1);
                                else if(y3<y1) now=1ll*min(r1,y2-y1-1)*c1*r2*c2*r3*c3;
                                else if(y1<y3&&y3<y2) now=1ll*c1*r2*c2*r3*(min(r1,y2-y1-1)*c3-max((y1+min(r1,y2-y1-1))-y3+1,0)*max(x1-(x3-c3)+1,0));
                                else if(y3>y2) now=1ll*min(r1,y2-y1-1)*c1*c2*r3*(r2*c3-max((y2+r2)-y3+1,0)*max(x2-(x3-c3)+1,0));
                            }
                            else if(x2==x3){
                                if(y1==y2) now=1ll*c1*min(r2,y3-y2-1)*min(c2,x2-x1-1)*r3*(r1*c3-max((y1+r1)-y3+1,0)*max(x1-(x3-c3)+1,0));
                                else if(y1==y3) now=1ll*r1*c1*min(r2,y3-y2-1)*c2*r3*min(c3,x3-x1-1);
                                else if(y1<y2){
                                    now=1ll*c1*min(r2,y3-y2-1)*r3*(max((y1+r1)-y3+1,0)*min(c2,x2-x1-1)*min(c3,x3-x1-1)
                                    +max((y1+min(r1,y3-y1-1))-y2+1,0)*min(c2,x2-x1-1)*c3
                                    +min(r1,y2-y1-1)*c2*c3);
                                }
                                else if(y2<y1&&y1<y3) now=1ll*c1*min(r2,y3-y2-1)*c2*r3*(r1*c3-max((y1+r1)-y3+1,0)*max(x1-(x3-c3)+1,0));
                                else if(y1>y3) now=1ll*r1*c1*min(r2,y3-y2-1)*c2*r3*c3;
                            }
                            else{
                                if(y1==y2&&y2==y3) now=1ll*r1*c1*r2*min(c2,x2-x1-1)*r3*min(c3,x3-x2-1);
                                else if(y1==y2){
                                    if(y3<y1) now=1ll*r1*c1*r2*min(c2,x2-x1-1)*r3*c3;
                                    else if(y3>y1){
                                        now=1ll*c1*min(c2,x2-x1-1)*r3*(max(x1-(x3-c3)+1,0)*min(r1,y3-y1-1)*min(r2,y3-y2-1)
                                        +max(x2-(x3-min(c3,x3-x1-1))+1,0)*r1*min(r2,y3-y2-1)
                                        +min(c3,x3-x2-1)*r1*r2);
                                    }
                                }
                                else if(y1==y3){
                                    if(y2<y1) now=1ll*r1*c1*c2*r3*(r2*min(c3,x3-x1-1)-max((y2+r2)-y3+1,0)*max(x2-(x3-min(c3,x3-x1-1))+1,0));
                                    else if(y2>y1) now=1ll*c1*r2*r3*min(c3,x3-x1-1)*(r1*c2-max((y1+r1)-y2+1,0)*max(x1-(x2-c2)+1,0));
                                }
                                else if(y2==y3){
                                    if(y1<y2) now=1ll*c1*r2*r3*min(c3,x3-x2-1)*(r1*c2-max((y1+r1)-y2+1,0)*max(x1-(x2-c2)+1,0));
                                    else if(y1>y2) now=1ll*r1*c1*r2*c2*r3*min(c3,x3-x2-1);
                                }
                                else{
                                    if(y1<y2&&y2<y3){
                                        now=1ll*c1*r3*(r1*r2*c2*c3
                                        -r2*c3*max((y1+r1)-y2+1,0)*max(x1-(x2-c2)+1,0)
                                        -r1*c2*max((y2+r2)-y3+1,0)*max(x2-(x3-c3)+1,0)
                                        -r2*c2*max((y1+r1)-y3+1,0)*max(x1-(x3-c3)+1,0)
                                        +r2*max((y1+r1)-y3+1,0)*max(x1-(x2-c2)+1,0)*max(x1-(x3-c3)+1,0)
                                        +max((y1+r1)-y2+1,0)*max(x1-(x2-c2)+1,0)*max((y2+r2)-y3+1,0)*max(x2-(x3-c3)+1,0)
                                        +c2*max((y1+r1)-y3+1,0)*max((y2+r2)-y3+1,0)*max(x1-(x3-c3)+1,0)
                                        -max(x1-(x2-c2)+1,0)*max(x1-(x3-c3)+1,0)*max((y1+r1)-y3+1,0)*max((y2+r2)-y3+1,0));
                                    }
                                    else if(y1<y3&&y3<y2){
                                        now=1ll*c1*r2*r3*(max((y1+r1)-y2+1,0)*min(c3,x3-x1-1)*min(c2,x2-x1-1)
                                        +max((y1+min(r1,y2-y1-1))-y3+1,0)*min(c3,x3-x1-1)*c2
                                        +min(r1,y3-y1-1)*c3*c2);
                                    }
                                    else if(y2<y1&&y1<y3){
                                        now=1ll*c1*c2*r3*(max(x1-(x3-c3)+1,0)*min(r1,y3-y1-1)*min(r2,y3-y2-1)
                                        +max(x2-(x3-min(c3,x3-x1-1))+1,0)*r1*min(r2,y3-y2-1)
                                        +min(c3,x3-x2-1)*r1*r2);
                                    }  
                                    else if(y2<y3&&y3<y1) now=1ll*r1*c1*c2*r3*(r2*c3-max((y2+r2)-y3+1,0)*max(x2-(x3-c3)+1,0));
                                    else if(y3<y1&&y1<y2) now=1ll*c1*r2*r3*c3*(r1*c2-max((y1+r1)-y2+1,0)*max(x1-(x2-c2)+1,0));
                                    else now=1ll*r1*c1*r2*c2*r3*c3;
                                }
                            }
                            ans+=now;
                        }
                    }
                }
            }
        }
    }
    printf("%lld\n",ans);
    return 0;
}

正解是插头 DP。

注意到插头只有三个,直接 \(O(nm^3)\),状态记录上插头位置以及是否有左插头即可。

点击查看代码
int n,m;
char mp[35][35];
int ex,ey;
int id[35][35][35],cnt;
struct Data{
    int a,b,c;
    Data()=default;
    Data(int a_,int b_,int c_):a(a_),b(b_),c(c_){}
}S[31*31*31+10];
inline int get_id(int a,int b,int c){
    int cnt0=0;
    if(!a) ++cnt0;
    if(!b) ++cnt0;
    if(!c) ++cnt0;
    if(cnt0==3) return id[0][0][0];
    else if(cnt0==2) return id[max({a,b,c})][0][0];
    else if(cnt0==1) return id[a+b+c-max({a,b,c})][max({a,b,c})][0];
    else return id[min({a,b,c})][a+b+c-max({a,b,c})-min({a,b,c})][max({a,b,c})];
}
ll dp[2][31*31*31+10][2][4],ans;
int main(){
    freopen("logo.in","r",stdin);
    freopen("logo.out","w",stdout);
    n=read(),m=read();
    for(int i=1;i<=n;++i){
        scanf("%s",mp[i]+1);
        for(int j=1;j<=m;++j){
            if(mp[i][j]=='.') ex=i,ey=j;
        }
    }
    id[0][0][0]=++cnt,S[cnt]=Data(0,0,0);
    for(int a=1;a<=m;++a) id[a][0][0]=++cnt,S[cnt]=Data(a,0,0);
    for(int a=1;a<=m;++a){
        for(int b=a+1;b<=m;++b){
            id[a][b][0]=++cnt,S[cnt]=Data(a,b,0);
        }
    }
    for(int a=1;a<=m;++a){
        for(int b=a+1;b<=m;++b){
            for(int c=b+1;c<=m;++c){
                id[a][b][c]=++cnt,S[cnt]=Data(a,b,c);
            }
        }
    }
    int now=0;
    dp[now][get_id(0,0,0)][0][0]=1;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            now^=1;
            for(int s=1;s<=cnt;++s){
                for(int p=0;p<=1;++p){
                    for(int k=0;k<=3;++k){
                        dp[now][s][p][k]=0;
                    }
                }
            }
            for(int s=1;s<=cnt;++s){
                for(int k=0;k<=3;++k){
                    ll v0=dp[now^1][s][0][k],v1=dp[now^1][s][1][k];
                    if(!v0&&!v1) continue;
                    int a=S[s].a,b=S[s].b,c=S[s].c;
                    // cerr<<"i:"<<i<<" j:"<<j<<" a:"<<a<<" b:"<<b<<" c:"<<c<<" k:"<<k<<" 0:"<<v0<<" 1:"<<v1<<endl;
                    if(mp[i][j]=='#'){
                        if(j!=a&&j!=b&&j!=c) dp[now][s][0][k]+=v0;
                    }
                    else{
                        if(j!=a&&j!=b&&j!=c){
                            dp[now][s][0][k]+=v0+v1;
                            if(mp[i][j+1]=='.') dp[now][s][1][k]+=v1;
                            if(!c&&mp[i+1][j]=='.'&&k<3) dp[now][get_id(a,b,j)][0][k+1]+=v0;
                            if(i==ex&&j==ey&&k==3) ans+=v0+v1;
                        }
                        else{
                            if(mp[i+1][j]=='.') dp[now][s][0][k]+=v0;
                            if(mp[i][j+1]=='.') dp[now][get_id((j==a)?0:a,(j==b)?0:b,(j==c)?0:c)][1][k]+=v0;
                        }
                    }
                }
            }
        }
    }
    printf("%lld\n",ans);
    return 0;
}

7.7 冲刺国赛模拟 32

T1 树

考虑 DP,连通性使用经典容斥,设 \(f_{S,i}\) 为当前 \(1\) 所在连通块为 \(S\),且选中边数为 \(i\) 的方案数,设 \(cnt_S\) 为点集为 \(S\) 时可选的边数,转移减去不连通(一部分连通剩下边随便选)的情况:

\[f_{S_i}=\dbinom{cnt_S}{i}-\sum_{\substack{1\in T\\T\subsetneq S}}\sum_{j=0}^{i} f_{T,j}\dbinom{cnt_{S\setminus T}}{i-j} \]

这样复杂度 \(O(3^nm^2)\)

观察这个转移方程,是一个子集卷积搭配多项式卷积的形式,考虑把 \(f_S\) 整体看作多项式,于是:

\[f_S=(1+x)^{cnt_S}-\sum_{\substack{1\in T\\T\subsetneq S}} f_{T}(1+x)^{cnt_{S\setminus T}} \]

\(y=1+x\),后面就变成枚举子集后多项式平移。算出 \(y\) 的系数再把 \(1+x\) 代入展开即可,复杂度 \(O(3^nm+m^2)\)

点击查看代码
int n,m;
pii e[maxm];
int cnt[maxs];
int C[maxm][maxm];
int f[maxs][maxm];
int ans[maxm];

int main(){
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    n=read(),m=read();
    C[0][0]=1;
    for(int i=1;i<=m;++i){
        C[i][0]=C[i][i]=1;
        for(int j=1;j<i;++j){
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
        }
    }
    for(int i=1;i<=m;++i){
        e[i].fir=read()+1,e[i].sec=read()+1;
    }
    for(int s=0;s<(1<<n);++s){
        for(int i=1;i<=m;++i){
            int u=e[i].fir,v=e[i].sec;
            if(s&(1<<u-1)&&s&(1<<v-1)) ++cnt[s];
        }
    }
    for(int s=0;s<(1<<n);++s){
        if(!(s&1)) continue;
        f[s][cnt[s]]=1;
        for(int t=s-1;t;t=(t-1)&s){
            if(!(t&1)) continue;
            for(int i=cnt[s^t];i<=cnt[s];++i){
                f[s][i]=(f[s][i]-f[t][i-cnt[s^t]]+mod)%mod;
            }
        }
    }
    for(int i=0;i<=m;++i){
        for(int j=0;j<=i;++j){
            ans[j]=(ans[j]+1ll*f[(1<<n)-1][i]*C[i][j]%mod)%mod;
        }
    }
    for(int i=n-1;i<=m;++i) printf("%d ",ans[i]);
    printf("\n");
    return 0;
}

T3 贼

以下是口胡题解。

答案是 \(\sum_{i=l}^r \min(\mathrm{lcp}(l,i),r-i+1)\),建反串 SAM 得到后缀树,这样 \(\mathrm{lcp}\) 就是后缀树上对应节点 \(\mathrm{LCA}\)\(\mathrm{len}\) 值。

考虑点分治,\(l\)\(i\) 对应节点在实际只有两种情况:\(i\) 是否在当前分治中心后缀树的子树内。如果在子树内,那么 \(\mathrm{LCA}\) 只和 \(l\) 有关,这样每次询问都是在数 \(i\) 的范围,线段树或树状数组即可;反之则只和 \(i\) 有关,这样数点就有 \(i\) 以及 \(i\) 与分治中心的 \(\mathrm{LCA}\) 有关,这是固定的,可以二维数点。

但是很难写,且空间大致要 1024MB 以上。

posted @ 2023-07-02 21:45  SoyTony  阅读(35)  评论(0编辑  收藏  举报