【考试总结】2022-05-29
s1mple
将长度为 的 串压成二进制数字,预处理所有询问的答案
尝试求出 ,也就是说钦定一些位置为 ,剩下的位置 不限,最后做 得到答案
使用钦定的 将元素连成一些段,由于没被钦定的 在排列确定时的数值的确定的,那么某个 就是 分成的若干段的方案数的乘积
那么对于分成的段的集合相同的 有 ,所以本质不同的 只有 个
做平凡 求出来每个集合 能形成的单链数 ,用占位幂级数的思想设集合幂级数
此时对于每个集合划分将每个段长对应的集合幂级数做或卷积,不难发现直接使用 全集处的系数 就是结果
直接实现复杂度是 ,如果不每个划分做 而是用定义式求单点应该可以往下降
Code Display
const int N=17;
char s[N+2][N+2],q[N+2];
int n,Q,U;
int ans[1<<N];
int one[N+1],id[1<<N];
int F[N+1][1<<N],met[1<<N][N+1];
inline void FWT_Or(int *f,int lim){
for(int p=2;p<=lim;p<<=1){
int len=p>>1;
for(int k=0;k<lim;k+=p) for(int l=k;l<k+len;++l) f[l+len]+=f[l];
}
return ;
}
inline void iFWT_And(int *f,int lim){
for(int p=2;p<=lim;p<<=1){
int len=p>>1;
for(int k=0;k<lim;k+=p) for(int l=k;l<k+len;++l) f[l]-=f[l+len];
}
return ;
}
inline void iFWT_Or(__int128 *f,int lim){
for(int p=2;p<=lim;p<<=1){
int len=p>>1;
for(int k=0;k<lim;k+=p) for(int l=k;l<k+len;++l) f[l+len]-=f[l];
}
return ;
}
inline ull get_hs(vector<int> &x){
sort(x.begin(),x.end());
ull hs=0;
for(auto t:x) hs=hs*13331+t;
return hs;
}
map<ull,int> mp;
__int128 f[1<<N];
inline int solve(int S){
vector<int> sta;
int cnt=0;
for(int i=1;i<=n-1;++i){
if(S>>(i-1)&1) ++cnt;
else{
sta.emplace_back(cnt+1);
cnt=0;
}
}
sta.emplace_back(cnt+1);
ull curhs=get_hs(sta);
if(mp.count(curhs)) return mp[curhs];
rep(i,0,U) f[i]=1;
for(auto k:sta){
rep(i,0,U) f[i]*=F[k][i];
}
iFWT_Or(f,U+1);
return mp[curhs]=f[U];
}
signed main(){
freopen("s1mple.in","r",stdin); freopen("s1mple.out","w",stdout);
n=read();
U=(1<<n)-1;
for(int i=1;i<=n;++i) id[1<<(i-1)]=i;
rep(i,1,n){
scanf("%s",s[i]+1);
for(int j=1;j<=n;++j){
if(s[i][j]=='1') one[i]|=1<<(j-1);
}
}
rep(i,1,n) met[(1<<(i-1))][i]=1;
for(int s=1;s<U;++s){
for(int j=1;j<=n;++j) if(met[s][j]){
int S=(U^s)&one[j];
while(S){
int lb=S&(-S),cur=id[lb];
met[s|lb][cur]+=met[s][j];
S-=lb;
}
}
}
for(int s=1;s<=U;++s){
int pc=__builtin_popcount(s);
for(int j=1;j<=n;++j) F[pc][s]+=met[s][j];
}
for(int i=1;i<=n;++i) FWT_Or(F[i],U+1);
for(int i=0;i<(1<<(n-1));++i) ans[i]=solve(i);
iFWT_And(ans,1<<(n-1));
Q=read();
while(Q--){
scanf("%s",q+1);
int num_q=0;
rep(i,1,n-1) num_q|=(q[i]-'0')<<(i-1);
print(ans[num_q]);
}
return 0;
}
s2mple
将 扩展成 ,若 仍然是 的子串,那么给答案贡献 ,也就是说统计可空的字符串对 <pre,suf>
的个数
正确性在于如果 在某个子串上出现了多次,那么找到所有 ,每个结束位置向左向右补上所缺就能得到一种方案
对于在字符串前面加字符本质上是走到其后缀 树的子树里面,而在串后添加字符的方案数是 在 构成的 上求有多少本质不同的到达后继节点的路径数
使用拓扑排序求出来后者 ,对其做子树的 的和
使用倍增找到 对应的节点,注意这个点本身对应的所有字符串不一定都是比 长的,所以要找到长度不小于 的部分
Code Display
const int N=8e5+10;
int pos[N],len[N],fa[N],son[N][26],tot=1,las=1;
inline void extend(int x,int id){
int tmp=las,np=las=++tot; pos[id]=np;
len[np]=len[tmp]+1;
while(!son[tmp][x]) son[tmp][x]=np,tmp=fa[tmp];
if(!tmp) return fa[np]=1,void();
int q=son[tmp][x];
if(len[q]==len[tmp]+1) return fa[np]=q,void();
int clone=++tot; len[clone]=len[tmp]+1;
fa[clone]=fa[q]; fa[np]=fa[q]=clone;
rep(i,0,25) son[clone][i]=son[q][i];
while(son[tmp][x]==q) son[tmp][x]=clone,tmp=fa[tmp];
}
int val[N],n,Q;
char s[N];
int in[N],bz[N][20];
vector<int> G[N],pre[N];
inline int find(int l,int r){
int x=pos[r];
for(int i=19;~i;--i) if(len[bz[x][i]]>=r-l+1) x=bz[x][i];
return x;
}
inline void dfs(int x){
bz[x][0]=fa[x];
for(int i=1;bz[x][i-1];++i) bz[x][i]=bz[bz[x][i-1]][i-1];
for(auto t:G[x]) dfs(t);
return ;
}
int sub[N];
signed main(){
freopen("s2mple.in","r",stdin);
freopen("s2mple.out","w",stdout);
n=read(); Q=read(); scanf("%s",s+1);
for(int i=1;i<=n;++i) extend(s[i]-'a',i);
for(int i=2;i<=tot;++i) G[fa[i]].emplace_back(i);
dfs(1);
for(int i=1;i<=tot;++i){
rep(j,0,25) if(son[i][j]){
pre[son[i][j]].emplace_back(i);
in[i]++;
}
}
queue<int> q;
for(int i=1;i<=tot;++i) if(!in[i]) q.push(i);
while(q.size()){
int fr=q.front(); q.pop();
val[fr]++;
for(auto t:pre[fr]){
if(!(--in[t])) q.push(t);
val[t]+=val[fr];
}
}
function<void(int)>get_sub=[&](const int x){
for(auto t:G[x]) get_sub(t),sub[x]+=sub[t];
sub[x]+=(len[x]-len[fa[x]])*val[x];
};
get_sub(1);
while(Q--){
int l=read(),r=read();
int pos=find(l,r);
int ans=sub[pos]-(len[pos]-len[fa[pos]])*val[pos];
print(ans+(len[pos]-(r-l+1)+1)*val[pos]);
}
return 0;
}
s3mple
设 表示 都大于 中元素的情况下 的方案数
注意到前两维对 的决定作用只和 有关,所以将状态定义为 再转移:
写成 的形式有
观察到 最大是 级别,写一个 可以发现在 只有
那么使用拉格朗日插值还原多项式的方法得到答案即可,即先带入 做上面的 再用公式展开还原
还原时有除多项式的问题,可以用大除法模拟
一个降低多项式次数的方法是发现转移的过程中必然有 ,那么把所有转移的距离中的 删掉,让询问的 也减少 ,那么次数上界变成
Code Display
const int N=210,M=600;
int inv[M],ifac[M],pw[M][N],C[N][N];
int n,x;
vector<int> f[N];
vector<int> pre[M],suf[M];
int lim[N];
signed main(){
freopen("s3mple.in","r",stdin); freopen("s3mple.out","w",stdout);
mod=read();
if(mod==1){
while(~scanf("%lld%lld",&n,&x)) puts("0");
exit(0);
}
C[0][0]=1;
rep(i,1,200){
C[i][0]=1;
rep(j,1,i){
C[i][j]=add(C[i-1][j],C[i-1][j-1]);
ckmax(lim[i],lim[j-1]+lim[i-j]+min(j-1,i-j));
}
}
inv[0]=inv[1]=ifac[0]=ifac[1]=1;
rep(i,2,550){
inv[i]=mod-mul(mod/i,inv[mod%i]);
ifac[i]=mul(ifac[i-1],inv[i]);
}
for(int i=1;i<=550;++i){
pw[i][0]=1;
for(int j=1;j<=200;++j) pw[i][j]=mul(pw[i][j-1],i);
}
rep(i,0,200) f[i].resize(551);
rep(i,1,550) f[0][i]=1;
for(int i=1;i<=200;++i){
for(int j=0;j<i;++j){
for(int k=1;k<=550;++k){
int coef=mul(C[i-1][j],pw[k][min(j,i-j-1)]);
int val=mul(f[j][k],f[i-j-1][k]);
ckadd(f[i][k],mul(coef,val));
}
}
}
while(~scanf("%lld%lld",&n,&x)){
x-=n;
if(x<0||x>lim[n]){
puts("0");
continue;
}
auto Mul=[&](const vector<int> a,const vector<int> b){
vector<int> c; c.resize(a.size()+b.size()-1);
for(int i=0;i<a.size();++i){
for(int j=0;j<b.size();++j) ckadd(c[i+j],mul(a[i],b[j]));
}
return c;
};
int ans=0;
pre[0]=suf[551]={1};
for(int i=1;i<=550;++i) pre[i]=Mul(pre[i-1],{mod-i,1});
for(int i=550;i>=1;--i) suf[i]=Mul(suf[i+1],{mod-i,1});
for(int i=1;i<=550;++i){
int coef=f[n][i];
ckmul(coef,mul(ifac[550-i],ifac[i-1]));
if((550-i)&1) ckmul(coef,mod-1);
ckadd(ans,mul(coef,Mul(pre[i-1],suf[i+1])[x]));
}
print(ans);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律