2021牛客暑期多校训练营10 部分题题解
A.Browser Games
题目链接
简要题解
这道题卡空间,因此处理起来需要多费功夫。
我们先考虑不卡空间的话,该怎么做。
不难想到,首先要对所有串建一棵\(Trie\)树,然后在\(Trie\)树上维护一些东西。
具体地说,我们先将所有字符串染成黑色,然后第\(i\)天会把第\(i\)个串染成白色,并计算答案。
对\(Trie\)树上的每个节点,记录当前时刻,有多少个黑串和白串经过。
我们的任务是选出最少的关键点,使得每个白串上都必须要有关键点,每个黑串上都不能有关键点。
那么对于每一天来说,我们首先将当天串的串尾记为关键点,然后将关键点尽可能地往根移动即可。
如果限制了空间怎么办?
我们观察发现,对于\(Trie\)树上的某个节点来说,如果它只有一个儿子,那么它的儿子肯定不会成为关键点,因为我们会把关键点尽可能往根移动。
于是我们就可以把这个点和它的儿子缩成一个点。
空间复杂度分析,由于任何一个串都不是其他串的前缀,所以在\(Trie\)树上,每个点一定会对应\(1\)个叶子结点。
我们倒过来考虑,已经有了\(n\)个叶子结点,将它们不断合并,最终合并成一棵\(Trie\)树。
不难发现,每次合并两棵子树,只会增加一个结点,因此整棵树只会有大约\(2*n\)个结点。
关于如何建出这棵\(Trie\)树,首先对所有串按字典序排序,那么经过\(Trie\)树上某个节点的串一定是一段连续的区间。
这样我们就能够轻易记录,到达某个结点时,还有哪些串了,递归建树即可。
具体实现,这里的方法是,用\(set\)记录了每个点的儿子。
代码如下:
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
const int MAXN=1e5+1;
char Str[MAXN][101];
int Ans,n,Ss,End[MAXN],Ord[MAXN];
vector<int>Bin,Fa,Pass;
vector<set<int>>Son;
vector<bool>Col;
set<int>Zero;
bool cmp(int A,int B){ return strcmp(Str[A]+1,Str[B]+1)<0; }
void New(int Nf,int Np){ Son.pb(Zero),Fa.pb(Nf),Pass.pb(Np),Col.pb(0); }
void Build(int S,int D,int Le,int Ri)
{ if(Le==Ri) return End[Ord[Le]]=S,(void)0;
for(int i=Le,j=i;i<=Ri;i=++j)
{ while(j+1<=Ri&&Str[Ord[j+1]][D]==Str[Ord[i]][D]) j++;
while(Str[Ord[i]][D]==0&&i<=j) i++;
if(i==Le&&j==Ri&&S) Build(S,D+1,Le,Ri);
else if(i<=j) New(S,j-i+1),Build(++Ss,D+1,i,j);
}
}
void Add(int Np){ Col[Np]=1,Ans++,Son[Fa[Np]].insert(Np); }
void Del(int Np){ Col[Np]=0,Ans--,Son[Fa[Np]].erase(Np); }
int main()
{ scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%s",Str[i]+1),Ord[i]=i;
sort(Ord+1,Ord+n+1,cmp),New(0,n),Build(0,1,1,n);
for(int i=1;i<=n;i++)
{ Add(End[i]);
for(int j=End[i];j;j=Fa[j]) Pass[j]--;
for(int j=End[i];j;j=Fa[j])
if(Pass[Fa[j]]) j=0;
else if(Col[Fa[j]]) Del(j);
else
{ for(int v:Son[Fa[j]]) Bin.pb(v);
for(int v:Bin) Del(v);
Bin.clear(),Add(Fa[j]);
}
printf("%d\n",Ans);
}
}
G.Game of Death
题目链接
简要题解
我们设\(S\)表示某种情况下,被射中的人的集合,再设\(F[S]\)表示,该情况发生的概率。
这个直接求不太方便,但是实际上,这道题是二项式反演的经典形式。
具体地说,我们设\(G[S]\)表示被射中的人是\(S\)子集的概率,那么根据二项式反演,我们有
不难发现,\(F[S]\)和\(G[S]\)的值只和\(|S|\)有关,于是我们重新设\(F[i]\)表示,恰好\(i\)个人被射中的概率,\(G[i]\)表示不超过\(i\)个人被射中的概率。
这个式子可以拆成多项式卷积的形式进行优化,现在我们只需要知道\(G[i]\)的值了。
考虑如何求\(G[i]\),既然是不超过\(i\)个人死,我们就只需要保证射中的子弹在这\(i\)个人里面即可。
对于外面的\(n-i\)个人,要么没射中,要么射中了\(i\)个人中的某一个,因此概率为\((1-p+p*\frac{i}{n-1})^{n-i}\)
对于里面的\(i\)个人,只是不能射自己,其他的分析同上,概率为\((1-p+p*\frac{i-1}{n-1})^i\)
于是
时间复杂度\(O(n*logn)\)
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int Mod=998244353;
const int MAXN=3e5+10;
int n,P,Q,Inv[MAXN],Fac[MAXN],F[MAXN],G[MAXN];
int Pow(int Down,int Up)
{ int Ret=1,Now=Down;
for(;Up>=1;Up>>=1) Up&1?Ret=1ll*Ret*Now%Mod:0,Now=1ll*Now*Now%Mod;
return Ret;
}
int Add(int A,int B){ return A+=B,A>=Mod?A-Mod:A; }
namespace Poly
{ int A[1<<20],B[1<<20],Rader[1<<20],Len,Ms,End,Invl;
void NTT(int *P,int K)
{ for(int i=0;i<Len;i++)
if(i<Rader[i]) swap(P[i],P[Rader[i]]);
for(int i=1;i<Len;i<<=1)
{ int Euler=Pow(3,(Mod-1)/(i<<1));
if(K<0) Euler=Pow(Euler,Mod-2);
for(int Pos=(i<<1),j=0;j<Len;j+=Pos)
{ int Wi=1;
for(int k=0;k<i;k++,Wi=1ll*Wi*Euler%Mod)
{ int X=P[k+j],Y=1ll*Wi*P[i+j+k]%Mod;
P[k+j]=Add(X,Y),P[i+j+k]=Add(X,Mod-Y);
}
}
}
if(K>0) return ;
Invl=Pow(Len,Mod-2);
for(int i=0;i<Len;i++) P[i]=1ll*P[i]*Invl%Mod;
}
void Prepare()
{ End=n*2,Ms=-1;
for(Len=1;Len<=End;Len<<=1) Ms++;
for(int i=0;i<Len;i++) Rader[i]=((Rader[i>>1]>>1)|((i&1)<<Ms));
}
void Work()
{ for(int i=0;i<=n;i++) A[i]=i&1?Mod-Inv[i]:Inv[i],B[i]=1ll*G[i]*Inv[i]%Mod;
NTT(A,1),NTT(B,1);
for(int i=0;i<Len;i++) A[i]=1ll*A[i]*B[i]%Mod;
NTT(A,-1);
for(int i=0;i<=n;i++) F[i]=1ll*Fac[i]*A[i]%Mod;
}
}using namespace Poly;
int C(int A,int B){ return B>A||A<0?0:1ll*Fac[A]*Inv[B]%Mod*Inv[A-B]%Mod; }
int main()
{ scanf("%d%d%d",&n,&P,&Q),P=1ll*P*Pow(Q,Mod-2)%Mod,Q=Add(Mod-P,1),Fac[0]=1;
for(int i=1;i<=n;i++) Fac[i]=1ll*Fac[i-1]*i%Mod;
Inv[n]=Pow(Fac[n],Mod-2);
for(int i=n-1;i>=0;i--) Inv[i]=1ll*Inv[i+1]*(i+1)%Mod;
for(int i=0,Invn=1ll*Inv[n-1]*Fac[n-2]%Mod;i<=n;i++)
G[i]=1ll*Pow((Q+1ll*P*(i-1)%Mod*Invn)%Mod,i)*Pow((Q+1ll*P*i%Mod*Invn)%Mod,n-i)%Mod;
Prepare(),Work();
for(int i=n;i>=0;i--) printf("%lld\n",1ll*F[i]*C(n,i)%Mod);
}