杂题选记

记录一些我觉得比较有意思的题目。难度差异可能会很大。

书信

给一个字符串 \(S\),对于 \(S\) 中的每一类字符,可以选择一个区间 \([l,r]\) 保留,保留的字母间相对顺序不变。

每一个位置有权值 \(w_i\)

求将 \(S\) 变为 \(T\) 的同时最小化删去位置的权值和。

解题思路

首先考虑刻画字母删除和保留。一个字母可以删除,则其前面/后面没有选择的相同字母。我们发现这个可以通过对 \(T\) 的匹配位数 \(j\) 来刻画。

于是设 \(f_{i,j}\) 表示 \(S[1,i]\) 匹配上 \(T[1,j]\) 的最小代价。

通过预处理,我们可以得知 \(L_c\)\(R_c\) 表示 \(c\)\(T\) 中出现的最前/最后位置。考虑当还没有用上 \(c\)\(j<L_c\))和已经用完 \(c\)\(j \geq R_c\))时可以删去 \(c\)

于是转移:

\[\begin{array}{c} [j+1 < L_{s[i+1]}+1 \vee j+1 > R_{s[i+1]}]: f_{i,j}+w_{i+1} \to f_{i+1,j} \\ [S_{i+1}=T_{j+1}]: f_{i,j} \to f_{i+1,j+1} \end{array} \]

可以获得 \(30 pts\)

考虑优化。可以发现第一个转移只和 \(j\) 以及 \(s[i+1]\)(具体的字母)有关。启发我们按照转移一的成立对 \(j\) 分组。具体来说,就是将所有的 \(L_c\)\(R_c+1\) 作为分界线,这样同一个段内的所有 \(j\) 对于相同的 \(c\) 的转移情况就是相同的了。

这样一共会分出 \(O(|\Sigma|)\) 个段。

同时,我们注意到段内的 \(j\)\(j+1\) 的转移中,\(f_{i,j}+w_{i+1} \to f_{i+1,j}\)\(f_{i,j} \to f_{i+1,j+1}\) 不同时成立。也即从 \((i,j_{1})\)\((?,j_{k})\) 的转移路径是唯一的。我们只要把那些 \(f_{i,j} \to f_{i+1,j+1}\) 的位置提出来,用字符串哈希判断转移路径是否可行即可。

时间复杂度就是 \(O(Tn|\Sigma|)\)

代码
#include<bits/stdc++.h>
#define int long long
#define fin(x) freopen(#x".in","r",stdin)
#define fout(x) freopen(#x".out","w",stdout)
#define file(x) (fin(x),fout(x),0)
using namespace std;
int My_File_IO=file(letter);
const int inf=1e16;
int n,m;
char s[200005],t[200005];
int w[200005],sumw;
namespace Hash{
    using ull=unsigned long long;
    using ui=__uint128_t;
    const ull mod=(1ull<<61)-1;
    const int bse=114514;
	inline ull pls(ull x,ull y){return (x+y>=mod?x+y-mod:x+y);}
    inline ull sub(ull x,ull y){return (x<y?x+mod-y:x-y);}
    inline ull mul(ull x,ull y){ui r=ui(x)*y;return pls(r>>61,r&mod);}
    ull fpow(ull a,ull b=mod-2){
        ull r=1;
        while(b){
            if(b&1)r=mul(r,a);
            a=mul(a,a);
            b>>=1;
        }
        return r;
    }
    ull pw[200005],ipw[200005];
    void init(){
        for(int i=pw[0]=1;i<=200000;i++)pw[i]=mul(pw[i-1],bse);
        ipw[200000]=fpow(pw[200000]);
        for(int i=199999;i>=0;i--)ipw[i]=mul(ipw[i+1],bse);
    }
    template<int siz>
    struct hsa{
        int usecnt;
        ull h[siz+1];
        void load(char *s,int n){for(int i=1;i<=n;i++)h[i]=pls(mul(h[i-1],bse),s[i]);usecnt=n;}
        void join(char s){usecnt++;h[usecnt]=pls(mul(h[usecnt-1],bse),s);}
        void cls(){usecnt=0;}
        ull cut(int l,int r){return sub(h[r],mul(h[l-1],pw[r-l+1]));}
    };
}
using Hash::hsa;
hsa<200005> hs,tmphs;
int fj[150],tot;
int f[200005];
int mn[30],mx[30];
void tmain(){
    cin>>n>>m>>(s+1)>>(t+1);
    for(int i=1;i<=n;i++)cin>>w[i],sumw+=w[i];
    hs.load(t,m);
    tot=sumw=0;
    for(int c=0;c<26;c++){
        mn[c]=m+1,mx[c]=0;
        for(int i=1;i<=m;i++)if(t[i]=='a'+c)mn[c]=min(mn[c],i),mx[c]=max(mx[c],i);
        if(mn[c]==m+1)continue;
        fj[++tot]=mn[c];
        fj[++tot]=mx[c]+1;
    }
    fj[++tot]=1;fj[++tot]=m+1;
    // for(int i=1;i<=m+1;i++)fj[++tot]=i;
    sort(fj+1,fj+1+tot);tot=unique(fj+1,fj+1+tot)-fj-1;
    f[0]=0;
    for(int i=1;i<=n;i++)f[i]=inf;
    for(int j=1;j<tot;j++){
        static int g[200005];
        fill(g,g+1+n,inf);
        for(int i=0;i<n;i++){
            if(fj[j]<mn[s[i+1]-'a']+1||fj[j]>mx[s[i+1]-'a'])f[i+1]=min(f[i+1],f[i]+w[i+1]);
            if(s[i+1]==t[fj[j]])g[i+1]=min(g[i+1],f[i]);
        }
        memcpy(f,g,(n+1)*sizeof(int));
        if(fj[j+1]==fj[j]+1)continue;
        tmphs.cls();
        int ct=fj[j+1]-fj[j]-1;
        static int sw[200005],ok[30],crs[200005];
        int tn=0;
        fill(g,g+1+n,inf);
        memcpy(sw,w,(n+1)*sizeof(int));
        memset(ok,0,sizeof ok);
        for(int c=0;c<26;c++)if(fj[j]<mn[c]||mx[c]<fj[j])ok[c]=1;
        for(int i=1;i<=n;i++)if(!ok[s[i]-'a'])sw[i]=0,crs[++tn]=i,tmphs.join(s[i]);
        for(int i=1;i<=n;i++)sw[i]+=sw[i-1];
        for(int i=0,ptr=1;i<n;i++){
            if(crs[ptr]<=i)ptr++;
            if(ptr+ct-1>tn)break;
            if(tmphs.cut(ptr,ptr+ct-1)!=hs.cut(fj[j]+1,fj[j+1]-1))continue;
            g[crs[ptr+ct-1]]=min(g[crs[ptr+ct-1]],f[i]+sw[crs[ptr+ct-1]]-sw[i]);
        }
        memcpy(f,g,(n+1)*sizeof(int));
    }
    for(int i=0;i<n;i++)f[i+1]=min(f[i+1],f[i]+w[i+1]);
    cout<<(f[n]>=inf?-1:f[n])<<'\n';
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    Hash::init();
    int tid,t;
    cin>>tid>>t;
    while(t--)tmain();
}

酒杯

在深度为 \(n\) 的满二叉树上放 \(m\) 个棋子(棋子可以放在同一个点上),每一层都有至少一个棋子的方案数。对 \(10^9+7\) 取模。

解题思路

(好像爆标了,题解是 \(O(m^2\log{n})\) 的容斥+倍增优化 DP)

首先对于 \(n \leq 20,m = 1145141919\) 的这个点,明摆着引导你想一个 \(O(2^n \operatorname{polylog} m)\) 的做法。直接对每一层都有至少一个棋子这个条件容斥。

枚举随便选的集合得到:\(ans=\sum_{S}(-1)^{n-|S|}(\sum_{x \in S}2^{x})^{m}=\sum_{i=0}^{2^n-1}(-1)^{n-|i|}i^m\)\(|i|\) 表示 \(i\) 的 popcount。

直接写就是 \(O(2^n\log{m})\)

这部分的代码
namespace Sub2{
    #define pc __builtin_popcount
    void sol(){
        int ans=0;
        for(int i=0;i<(1<<n);i++){
            if((n-pc(i))&1)ans=(ans-fpow(i,m%(mod-1))+mod)%mod;
            else ans=(ans+fpow(i,m%(mod-1)))%mod;
        }
        cout<<ans<<'\n';
    }
}

出题人大概是想用这档部分分引导选手思考容斥并推出官解做法,但我们这里先不管他,我们忘记刚才的东西从头开始。

我们考虑使用 EGF 对每层棋子的放置情况进行刻画。对于深度为 \(i+1\) 的一层,其 EGF 为:

\[F_i(x) = \sum_{j \geq 1} \frac{x^j}{j!}(2^i)^j = e^{2^ix}-1 \]

答案即为 \(m![x^m]\prod_{i=0}^{n-1} F_i(x)\)

多项式乘法即可做到 \(O(M(m)n)\),其中 \(M(n)\) 为多项式乘法的复杂度。如果写的是任意模数 ntt 就是 \(O(nm\log{m})\)。在出题给定的数据(即 \(n,m\) 同阶)的情况下已经是正解的理论复杂度了。但是任意模数 ntt 的常数不太美妙,感觉一脸不可过。

进一步我们发现 \(F_i\) 都可以写成基础多项式 \(F(x)=e^{x}-1\) 的复合形式,即 \(F_i(x)=F(2^ix)\)。答案变为 \(m![x^m]\prod_{i=0}^{n-1} F(2^ix)\)

考虑对这个乘积式做 exp-ln。记 \(\hat{F}(x)=x^{-1}(e^{x}-1)\)。则:

\[\begin{array}{c} m![x^m]\prod_{i=0}^{n-1} F(2^ix) = m![x^m]\prod_{i=0}^{n-1} 2^ix \hat{F}(2^ix)\\ =m!\prod_{i=0}^{n-1} 2^i[x^{m-n}]\prod_{i=0}^{n-1} \hat{F}(2^ix)\\ =m!\prod_{i=0}^{n-1} 2^i[x^{m-n}]\exp \sum_{i=0}^{n-1} \ln\hat{F}(2^ix) \end{array} \]

复合是具有结合律的,我们可以先求出 \(G=\ln\hat{F}(x)\),然后再去复合 \(2^ix\)。这里直接暴力做暴力加和就是 \(O(nm)\) 的了。于是我们做到了 \(O(M(m)+nm)\)

但是有人说题解是 \(O(m^2\log{n})\) 的,我把 \(n\) 开到 \(1145141919810\) 你不就炸了吗?

注意到 \(\sum_{i=0}^{n-1} \ln\hat{F}(2^ix)\) 这个复合是可以快速做的。具体的,先求出 \(G\),然后这个东西就是 \(\sum_{i=0}^{n-1} \sum_{j=0}{m} g_j (2^ix)^j=\sum_{j=0}{m} g_j x^j \sum_{i=0}^{n-1}(2^i)^j=\sum_{j=0}{m} g_j x^j \sum_{i=0}^{n-1}(2^j)^i\)。最后面是等比数列求和可以 \(O(\log{n})\) 算出来。

于是,我们轻松做到了 \(O(M(m)+m\log{n})\)。根据你的实现方式做到 \(O(m(m+\log{n}))\)\(O(m\log{nm})\)

这部分的代码
namespace Sub1{
    poly f0;
    void sol(){
        f0.resize(m+1);
        for(int i=0;i<=m;i++)f0[i]=ifac[i+1];
        f0=ln(f0,m+1);
        for(int i=0;i<=m;i++)f0[i]=1ll*(1-fpow(2,1ll*n*i%(mod-1))+mod)%mod*fpow((1-fpow(2,i)+mod)%mod)%mod*f0[i]%mod;
        f0=exp(f0,m+1);
        int ans=fac[m];
        for(int i=0,pw=1;i<n;i++,pw=2ll*pw%mod)ans=1ll*ans*pw%mod;
        cout<<1ll*ans*f0[m-n]%mod;
    }
}

第k大mex

定义集合 \(S\) 的 kthmex 为集合 \(S\) 中第 \(k\) 个没有出现的正整数。

长度为 \(n\) 的序列 \(a\)\(q\) 次操作:

  • \([L,R]\) 中的 \(x\) 变为 \(y\)
  • \([L,R]\) 的 kthmex。

解题思路

咕咕咕。但是先放个抽象代码吧,是题解的思路。

非人类的代码
#include<bits/stdc++.h>
#define fin(x) freopen(#x".in","r",stdin)
#define fout(x) freopen(#x".out","w",stdout)
#define file(x) (fin(x),fout(x),0)
const int N=1e5,V=3e5,B=1667,T=5000;
#define blk(x,s) ((x-1)/s+1)
#define bll(x,s,v) (x>blk(v,s)?v+1:(x-1)*s+1)
#define blr(x,s,v) min(x*s,v)
using namespace std;
int My_File_IO=file(kthmex);
int lsh[V+5],tls;
int n,m,a[N+5];
struct Que{int op,l,r,x,y;}Q[N+5];
int p[(V+T-1)/T+2][(N+B-1)/B+2][N+5],ew[(V+T-1)/T+2],ewv[V+5];
int sy[(V+T-1)/T+2];
int fa[N+5],pt[(N+B-1)/B+2][V+5];
int find(int x){return (fa[x]==x?x:(fa[x]=find(fa[x])));}
inline int lowbit(int x){return x&-x;}
inline void add(int *d,int pos,int vl){for(;pos<=n+1;pos+=lowbit(pos))d[pos]+=vl;}
inline int que(int *d,int pos){int r=0;for(;pos;pos-=lowbit(pos))r+=d[pos];return r;}
#define fque(d,x) (que(d,n+1)-que(d,x))
void pd(int x){for(int i=bll(x,B,n);i<=blr(x,B,n);i++)a[i]=a[find(i)];}
void exc(int b,int l,int r,int x,int y){
    pd(b);
    for(int i=bll(b,B,n);i<=blr(b,B,n);i++)pt[b][a[i]]=n+1,fa[i]=i;
    pt[b][x]=n+1;
    for(int i=l;i<=r;i++)if(a[i]==x)a[i]=y;
    for(int i=blr(b,B,n);i>=bll(b,B,n);i--){
        if(pt[b][a[i]]<=blr(b,B,n))fa[i]=find(pt[b][a[i]]);
        pt[b][a[i]]=i;
    }
}
int wh[N+5],yl[N+5],xz[N+5],stot;
signed main(){
    double cloc=clock();
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i],lsh[++tls]=a[i],fa[i]=i;
    for(int i=1;i<=m;i++){
        cin>>Q[i].op>>Q[i].l>>Q[i].r>>Q[i].x;
        if(Q[i].op==1)cin>>Q[i].y,lsh[++tls]=Q[i].x,lsh[++tls]=Q[i].y;
    }
    sort(lsh+1,lsh+1+tls);tls=unique(lsh+1,lsh+1+tls)-lsh-1;
    for(int i=1;i<=tls;i++)sy[blk(i,T)]+=lsh[i]-lsh[i-1]-1;
    for(int i=1;i<=n;i++)a[i]=lower_bound(lsh+1,lsh+1+tls,a[i])-lsh;
    for(int i=1;i<=m;i++){
        if(Q[i].op==2)continue;
        Q[i].x=lower_bound(lsh+1,lsh+1+tls,Q[i].x)-lsh;
        Q[i].y=lower_bound(lsh+1,lsh+1+tls,Q[i].y)-lsh;
    }
    for(int j=1;j<=tls;j++)pt[blk(n,B)+1][j]=n+1;
    for(int j=1;j<=blk(tls,T);j++)add(p[j][blk(n,B)+1],n+1,blr(j,T,tls)-bll(j,T,tls)+1);
    for(int i=blk(n,B);i>=1;i--){
        for(int j=1;j<=tls;j++)pt[i][j]=pt[i+1][j];
        for(int j=1;j<=tls;j++)add(p[blk(j,T)][i],pt[i][j],1);
        for(int j=blr(i,B,n);j>=bll(i,B,n);j--){
            if(pt[i][a[j]]<=blr(i,B,n))fa[j]=find(pt[i][a[j]]);
            add(p[blk(a[j],T)][i],pt[i][a[j]],-1);
            pt[i][a[j]]=j;
            add(p[blk(a[j],T)][i],pt[i][a[j]],1);
        }
    }
    for(int id=1;id<=m;id++){
        if(Q[id].op==1){
            int l=Q[id].l,r=Q[id].r,x=Q[id].x,y=Q[id].y;
            if(x==y)continue;
            for(int i=1;i<=blk(n,B);i++){
                add(p[blk(x,T)][i],pt[i][x],-1);
                add(p[blk(y,T)][i],pt[i][y],-1);
                if(pt[i][x]>blr(i,B,n))pt[i][x]=n+1;
            }
            if(blk(l,B)==blk(r,B))exc(blk(l,B),l,r,x,y);
            else{
                exc(blk(l,B),l,blr(blk(l,B),B,n),x,y);
                exc(blk(r,B),bll(blk(r,B),B,n),r,x,y);
                for(int i=blk(l,B)+1;i<blk(r,B);i++){
                    int rr=blr(i,B,n);
                    if(pt[i][x]>rr)continue;
                    if(pt[i][y]>rr){
                        pt[i][y]=pt[i][x];pt[i][x]=n+1;
                        a[find(pt[i][y])]=y;
                    }
                    else{
                        fa[find(pt[i][x])]=find(pt[i][y]);
                        a[find(pt[i][y])]=y;
                        pt[i][y]=min(pt[i][y],pt[i][x]);
                        pt[i][x]=n+1;
                    }
                }
            }
            for(int i=blk(n,B);i>=1;i--){
                pt[i][x]=min(pt[i][x],pt[i+1][x]);
                pt[i][y]=min(pt[i][y],pt[i+1][y]);
                add(p[blk(x,T)][i],pt[i][x],1);
                add(p[blk(y,T)][i],pt[i][y],1);
            }
        }
        else{
            int l=Q[id].l,r=Q[id].r,k=Q[id].x;
            int b=blk(l,B);if(l!=bll(b,B,n))b++;
            for(int i=bll(b,B,n)-1;i>=l;i--){
                stot++;
                wh[stot]=a[find(i)];
                yl[stot]=pt[b][wh[stot]];
                xz[stot]=i;
                pt[b][wh[stot]]=i;
                if(yl[stot]>r&&xz[stot]<=r)ew[blk(wh[stot],T)]++,ewv[wh[stot]]=1;
                // add(p[blk(wh[stot],T)][b],yl[stot],-1);
                // add(p[blk(wh[stot],T)][b],xz[stot],1);
            }
            int ans=0,fl=-1;
            for(int i=1;i<=blk(tls,T);i++){
                if(k>fque(p[i][b],r)+sy[i]-ew[i])k-=fque(p[i][b],r)+sy[i]-ew[i],ans=lsh[blr(i,T,tls)];
                else{fl=i;break;}
            }
            if(fl==-1)ans+=k;
            else{
                for(int i=bll(fl,T,tls);i<=blr(fl,T,tls);i++){
                    if(lsh[i]-lsh[i-1]-1+(pt[b][i]>r&&!ewv[i])<k)k-=lsh[i]-lsh[i-1]-1+(pt[b][i]>r&&!ewv[i]),ans=lsh[i];
                    else{ans+=k;break;}
                }
            }
            cout<<ans<<'\n';
            while(stot){
                // add(p[blk(wh[stot],T)][b],yl[stot],1);
                // add(p[blk(wh[stot],T)][b],xz[stot],-1);
                pt[b][wh[stot]]=yl[stot];
                if(yl[stot]>r&&xz[stot]<=r)ew[blk(wh[stot],T)]--,ewv[wh[stot]]=0;
                stot--;
            }
        }
    }
    cerr<<(clock()-cloc)/CLOCKS_PER_SEC;
}
posted @ 2025-12-09 20:14  Aysct  阅读(2)  评论(0)    收藏  举报