杂题选记
记录一些我觉得比较有意思的题目。难度差异可能会很大。
书信
给一个字符串 \(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\)。
于是转移:
可以获得 \(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 为:
答案即为 \(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)\)。则:
复合是具有结合律的,我们可以先求出 \(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;
}

浙公网安备 33010602011771号