【考试总结】2022-07-04
字符串
如果某个 的子串 恰为 ,那么 的 集合包含 ,对于后缀同理
正反做两遍 ,每个点和其 连边形成两棵树,此时询问变成了正向树 的子树中有几个点 满足反向树 号节点的子树里面有
这是二维数点问题,可以使用可持久化线段树解决
Code Display
const int N=200010,M=N*40;
char s[N];
int rt[N],ls[M],rs[M],tot,cnt[M];
inline void insert(int &p,int pre,int l,int r,int pos){
p=++tot; ls[p]=ls[pre]; rs[p]=rs[pre]; cnt[p]=cnt[pre]+1;
if(l==r) return ;
int mid=(l+r)>>1;
if(pos<=mid) insert(ls[p],ls[pre],l,mid,pos);
else insert(rs[p],rs[pre],mid+1,r,pos);
}
inline int query(int u,int v,int l,int r,int st,int ed){
if(!u) return 0;
if(st<=l&&r<=ed) return cnt[u]-cnt[v];
int mid=(l+r)>>1,res=0;
if(st<=mid) res+=query(ls[u],ls[v],l,mid,st,ed);
if(ed>mid) res+=query(rs[u],rs[v],mid+1,r,st,ed);
return res;
}
int main(){
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
int T=read();
while(T--){
tot=0;
int n=read(),Q=read();
scanf("%s",s+1);
vector<vector<int> >Gp(n+2),Gs(n+2);
vector<int> prenxt(n+2),sufnxt(n+2);
vector<int> inp(n+2),ins(n+2),outp(n+2),outs(n+2);
vector<int> val(n+2);
Gp[0].emplace_back(1);
for(int j=0,i=2;i<=n;++i){
while(j&&s[j+1]!=s[i]) j=prenxt[j];
j+=(s[j+1]==s[i]);
prenxt[i]=j;
Gp[j].emplace_back(i);
}
sufnxt[n]=n+1;
Gs[n+1].emplace_back(n);
for(int j=n+1,i=n-1;i>=1;--i){
while(j!=n+1&&s[j-1]!=s[i]) j=sufnxt[j];
j-=s[j-1]==s[i];
sufnxt[i]=j;
Gs[j].emplace_back(i);
}
int tim_pre=0,tim_suf=0;
function<void(int)>dfs_pre=[&](int x){
inp[x]=++tim_pre;
for(auto t:Gp[x]) dfs_pre(t);
outp[x]=tim_pre;
};
function<void(int)>dfs_suf=[&](int x){
val[inp[x-1]]=ins[x]=++tim_suf;
for(auto t:Gs[x]) dfs_suf(t);
outs[x]=tim_suf;
};
dfs_pre(0);
dfs_suf(n+1);
for(int i=1;i<=n+1;++i) insert(rt[i],rt[i-1],1,n+1,val[i]);
while(Q--){
int x=read(),y=read();
y=n-y+1;
print(query(rt[outp[x]],rt[inp[x]-1],1,1+n,ins[y],outs[y]));
}
}
return 0;
}
计树
设 表示 子树让一个 的排列在 子树里面放,满足父亲标号小于儿子的方案树
再设 表示 子树里面点 分配的标号排位是 的方案数,转移是子树归并,枚举已经合并的子树里面的点 原来的位次和新的位次,小于它和大于它的数任意排列,乘两个组合数以及当前添加儿子 的 ,对于 子树里面的点也是一样
将 这维度固定那么 的转移过程和树形背包一样,复杂度是
使用这两者来求答案,在 两个点的 处来强制它们相邻,设两个方向 的儿子为 ,枚举 在 子树里面的排名和 在 子树中的排名,除了 值之外还要让两棵树合并,此时将归并进来的子树 的大小视为 得到组合数即可
实现的时候枚举已归并子树中的点,由于系数和在新归并儿子 的子树中的排名有关,那么对于每个排名统计 子树中所有点的权值,统一计算,复杂度也是 。在 根链上合并带来的系数在回溯过程中处理
Code Display
const int N=210;
int n,rt,siz[N];
int g[N],ans[N],dp[N][N][N],tmp[N];
int C[N][N];
vector<int> G[N],sub[N];
signed main(){
freopen("tree.in","r",stdin); freopen("tree.out","w",stdout);
n=200; C[0][0]=1;
for(int i=1;i<=n;++i){
C[i][0]=1;
for(int j=1;j<=i;++j) C[i][j]=add(C[i-1][j],C[i-1][j-1]);
}
n=read(); rt=read();
for(int i=1;i<n;++i){
int u=read(),v=read();
G[u].emplace_back(v);
G[v].emplace_back(u);
}
function<void(int,int)>dfs=[&](int x,int fat){
siz[x]=g[x]=1;
for(auto t:G[x]) if(t!=fat) dfs(t,x);
for(auto t:G[x]) if(t!=fat){
int sum=g[x]*dp[t][t][1]%mod*C[siz[x]+siz[t]-2][siz[t]-1]%mod*abs(x-t);
for(auto u:sub[x]){
for(int i=1;i<=siz[t];++i){
int coef=0;
for(auto v:sub[t]) coef+=abs(u-v)*dp[t][v][i];
coef%=mod;
if(!coef) continue;
int val=0;
for(int j=1;j<=siz[x];++j) if(dp[x][u][j]){
val+=dp[x][u][j]*C[i-1+j-2][i-1]%mod*C[siz[x]-j+siz[t]-i][siz[x]-j]%mod;
}
sum+=val%mod*coef%mod*2;
}
}
for(auto u:sub[x]){
rep(i,2,siz[x]) if(dp[x][u][i]){
rep(j,0,siz[t]){
tmp[i+j]+=dp[x][u][i]*C[i-2+j][j]%mod*C[siz[x]+siz[t]-i-j][siz[x]-i]%mod;
}
}
rep(i,1,siz[x]+siz[t]) dp[x][u][i]=mul(g[t],tmp[i]%mod),tmp[i]=0;
}
for(auto u:sub[t]){
rep(i,1,siz[t]) if(dp[t][u][i]){
rep(j,1,siz[x]) tmp[i+j]+=dp[t][u][i]*C[i-2+j][i-1]%mod*C[siz[x]+siz[t]-i-j][siz[x]-j]%mod;
}
rep(i,1,siz[x]+siz[t]) dp[x][u][i]=mul(g[x],tmp[i]%mod),tmp[i]=0;
sub[x].emplace_back(u);
}
siz[x]+=siz[t];
ans[x]=(ans[x]*g[t]%mod*C[siz[x]-2][siz[t]]+ans[t]*g[x]%mod*C[siz[x]-2][siz[t]-1]+sum)%mod;
ckmul(g[x],mul(g[t],C[siz[x]-1][siz[t]]));
}
dp[x][x][1]=g[x];
sub[x].emplace_back(x);
};
dfs(rt,0);
print(ans[rt]);
return 0;
}
数论
将 函数中的 变为枚举 ,满足 ,这也等价于
所以答案表达式变成了
设 ,此时答案变成了
拆开 中的括号得到 ,通过分治乘法可以得到答案 的分子分母
剩下的问题是求 ,使用 波斯坦-茉莉 算法解决即可
Code Display
const int N=3e6+10;
int r[N],W[N],inv[N];
inline void NTT(vector<int> &f,int lim,int opt){
f.resize(lim);
for(int i=0;i<lim;++i){
r[i]=r[i>>1]>>1|((i&1)?(lim>>1):0);
if(i<r[i]) swap(f[i],f[r[i]]);
}
for(int p=2;p<=lim;p<<=1){
int len=p>>1; W[0]=1; W[1]=ksm(3,(mod-1)/p);
if(opt==-1) W[1]=ksm(W[1],mod-2);
for(int j=2;j<len;++j) W[j]=mul(W[j-1],W[1]);
for(int k=0;k<lim;k+=p){
for(int l=k;l<k+len;++l){
int tt=mul(f[l+len],W[l-k]);
f[l+len]=del(f[l],tt);
ckadd(f[l],tt);
}
}
}
if(opt==-1) for(int i=0,tmp=ksm(lim,mod-2);i<lim;++i) ckmul(f[i],tmp);
return ;
}
inline poly Mul(poly a,poly b){
int n=a.size(),m=b.size(),lim=1;
while(lim<=(n+m)) lim<<=1;
NTT(a,lim,1); NTT(b,lim,1);
for(int i=0;i<lim;++i) ckmul(a[i],b[i]);
NTT(a,lim,-1);
a.resize(n+m-1);
return a;
}
inline poly Inv(poly a,int len){
if(len==1) return {ksm(a[0],mod-2)};
vector<int> G0=Inv(a,(len+1)>>1);
int lim=1; while(lim<=(len<<1)) lim<<=1;
vector<int> tmp,G;
rep(i,0,len-1) tmp.emplace_back(a[i]);
NTT(tmp,lim,1); NTT(G0,lim,1);
for(int i=0;i<lim;++i) G.emplace_back(mul(G0[i],del(2,mul(G0[i],tmp[i]))));
NTT(G,lim,-1); G.resize(len);
return G;
}
int n,t,m,p[N],fac[N],ifac[N];
inline int binom(int n,int k){return mul(fac[n],mul(ifac[k],ifac[n-k]));}
inline poly div_con(int l,int r){
if(l==r){
vector<int> ret(m+1);
for(int i=0,pw=1;i<=m;++i,ckmul(pw,p[l])){
ret[i]=mul(binom(m,i),pw);
if(i&1) ret[i]=del(0,ret[i]);
}
return ret;
}
int mid=(l+r)>>1;
return Mul(div_con(l,mid),div_con(mid+1,r));
}
inline int Recurrence(vector<int> f,vector<int> g,int n){
while(n){
if(n<g.size()){
g=Inv(g,g.size());
f=Mul(f,g);
return f[n];
}
vector<int> tmp=g;
for(int i=1;i<tmp.size();i+=2) tmp[i]=del(0,tmp[i]);
int lim=1,sizg=g.size();
int fb=f.size()+sizg,gb=sizg+sizg;
--fb; --gb;
while(lim<=sizg*2) lim<<=1;
NTT(f,lim,1); NTT(g,lim,1); NTT(tmp,lim,1);
for(int i=0;i<lim;++i) ckmul(f[i],tmp[i]),ckmul(g[i],tmp[i]);
NTT(f,lim,-1); NTT(g,lim,-1);
f.resize(fb); g.resize(gb);
int sizmx=-1;
for(int i=(n&1);i<f.size();i+=2) f[i/2]=f[i],sizmx=i/2;
f.resize(sizmx+1);
sizmx=-1;
for(int i=0;i<g.size();i+=2) g[i/2]=g[i],sizmx=i/2;
g.resize(sizmx+1);
n>>=1;
}
if(f.size()) return mul(f[0],ksm(g[0],mod-2));
else return 0;
}
signed main(){
freopen("math.in","r",stdin); freopen("math.out","w",stdout);
n=5e5; fac[0]=1;
for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
ifac[n]=ksm(fac[n],mod-2);
for(int i=n;i>=1;--i) ifac[i-1]=mul(ifac[i],i);
n=read(); t=read(),m=read();
for(int i=1;i<=t;++i) p[i]=read();
vector<int> zi(m*t+1);
for(int i=0;i<=m*t;++i){
zi[i]=binom(m*t,i);
if(i&1) zi[i]=del(0,zi[i]);
}
vector<int> mu=div_con(1,t);
print((Recurrence(zi,mu,n)));
return 0;
}
分类:
模拟赛乱写
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律