T1:你在跟朋友玩一个记忆游戏。
朋友首先给你看了n个长度相同的串,然后从中等概率随机选择了一个串。
每一轮你可以询问一个位置上的正确字符,如果能够凭借已有的信息确定出朋友所选的串,那么游戏就结束了,你的成绩就是所用的轮数。
由于你实在太笨,不会任何策略,因此你采用一种方法,每次等概率随机询问一个未询问过的位置的字符。
现在你想知道,在这种情况下,你猜出结果所需的期望次数。
状压DP,预处理一个f数组表示询问某个状态之后可以确定多少个串,然后暴力转移即可
Code:
#include<bits/stdc++.h>
#define db double
#define ll long long
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
const int S=1<<20;
char st[55][25];
ll same[S];
int s[S];
db f[S];
int main(){
int n=read();
for(int i=1;i<=n;i++) scanf("%s",st[i]);
int len=strlen(st[1]);
int all=(1<<len)-1;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) if(i!=j){
int ss=0;
for(int k=0;k<len;k++) if(st[i][k]==st[j][k]) ss|=(1<<k);
same[ss]|=(1ll<<(i-1));
}
for(int i=all;~i;i--)
for(int j=0;j<len;j++) if(i&(1<<j)) same[i^(1<<j)]|=same[i];
same[0]=(1LL<<n)-1;
for(int i=0;i<=all;i++)
for(int j=0;j<n;j++) if(same[i]&(1ll<<j)) ++s[i];
for(int i=all;~i;i--){
int ss=0;
for(int j=0;j<len;j++) if(!(i&(1<<j))) ++ss;
if(ss && s[i])
for(int j=0;j<len;j++)
if(!(i&(1<<j)))
f[i]+=((db)(s[i]-s[i^(1<<j)])/s[i]+(db)s[i^(1<<j)]/s[i]*(f[i^(1<<j)]+1.0))/ss;
}
printf("%.10f\n",f[0]);
return 0;
}
T2:
有n个点,每个点有参数d,表示其度数最多为d,求构成1,2,…,n个点的无向生成树的方案数
生成树有几种做法:矩阵树,生成函数,组合数学,purfer序列
本题“度数最多为d”的限制非常强,直接导致生成函数和矩阵树爆掉
考虑purfer序列,显然就是构成一个长度为i-2的purfer序列的方案数,又因为每一种序列都是合法的purfer序列,所以只需要构造一个普通序列即可
设
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k]表示考虑前i个点,j个点在purfer序列中,purfer序列长度为k
转移类似背包,枚举这个点在purfer序列中出现的次数l,则就是把l个板插到k个数中,用插板法可知选这个点的转移系数就为
C
k
+
l
k
C_{k+l}^{k}
Ck+lk
Code:
#include<bits/stdc++.h>
#define ll long long
#define mod 1000000007
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
inline ll add(ll x,ll y){x+=y;if(x>=mod) x-=mod;return x;}
inline ll dec(ll x,ll y){x-=y;if(x<0) x+=mod;return x;}
inline ll mul(ll x,ll y){return 1ll*x*y%mod;}
inline void inc(ll &x,ll y){x+=y;if(x>=mod) x-=mod;}
inline void Dec(ll &x,ll y){x-=y;if(x<0) x+=mod;}
inline void Mul(ll &x,ll y){x=1ll*x*y%mod;}
const int N=105;
int d[N];
ll dp[N][N][N],c[N][N];
int n;
inline void init(){
for(int i=0;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]);
}
}
inline void file(){
freopen("neuron.in","r",stdin);
freopen("neuron.out","w",stdout);
}
int main(){
init();n=read();
for(int i=1;i<=n;i++) d[i]=read();
dp[0][0][0]=1;
init();
for(int i=0;i<=n;i++)
for(int j=0;j<=i;j++)
for(int k=0;k<=n-2;k++)
if(dp[i][j][k]){
inc(dp[i+1][j][k],dp[i][j][k]);
for(int l=0;l<=d[i+1]-1 && l+k<=n-2;l++) inc(dp[i+1][j+1][k+l],mul(dp[i][j][k],c[k+l][k]));
}
cout<<n<<" ";
for(int i=2;i<=n;i++) cout<<dp[n][i][i-2]<<" ";
return 0;
}
T3:动态维护一个字符串的区间不同子串个数,支持末尾加字符
把所有last相同的点放到一棵splay中,则一次修改就是access,然后套个主席树维护答案即可
就是有点长
#include<bits/stdc++.h>
#define ll long long
#define pi pair<ll,int>
#define last last2
#define fi first
#define se second
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
const int N=2e5+5;
char s[N];
int L[N<<1],rt1[N],rt2[N],len;
struct President_tree{
struct seg{int l,r,siz;ll sum;}tr[15000000];
#define ls(k) tr[k].l
#define rs(k) tr[k].r
#define mid (l+r>>1)
int cnt;
inline int cpy(int k){cnt++;tr[cnt]=tr[k];return cnt;}
int update(int k,int l,int r,int ql,int qr,int v){
k=cpy(k);
if(ql<=l && r<=qr){tr[k].sum+=v;++tr[k].siz;}
else{
if(ql<=mid) ls(k)=update(ls(k),l,mid,ql,qr,v);
if(mid<qr) rs(k)=update(rs(k),mid+1,r,ql,qr,v);
}
return k;
}
pi query(int k,int l,int r,int pos){
if(!k) return pi(0,0);
if(l==r) return pi(tr[k].sum,tr[k].siz);
pi ans;
if(pos<=mid) ans=query(ls(k),l,mid,pos);
else ans=query(rs(k),mid+1,r,pos);
return pi(ans.fi+tr[k].sum,ans.se+tr[k].siz);
}
}tr1,tr2;
namespace LCT{
int ls[N],rs[N],fa[N],last[N];
inline void copy(int x,int y){last[x]=last[y];}
inline int isrs(int x){return rs[fa[x]]==x;}
inline bool isroot(int x){
if(!x) return true;
return ls[fa[x]]!=x && rs[fa[x]]!=x;
}
inline void rotate(int x){
int y=fa[x],z=fa[y],b=ls[y]==x?rs[x]:ls[x];
if(z && !isroot(y)) (ls[z]==y?ls[z]:rs[z])=x;
fa[x]=z,fa[y]=x;b?fa[b]=y:0;
if(ls[y]==x) rs[x]=y,ls[y]=b;
else ls[x]=y,rs[y]=b;
}
inline void splay(int x){
int rt=x;
while(!isroot(rt)) rt=fa[rt];
swap(last[rt],last[x]);
while(!isroot(x)){
while(!isroot(fa[x])){
if(isrs(x)==isrs(fa[x])) rotate(fa[x]);
else rotate(x);
}
rotate(x);
}
}
inline void access(int x,int id){
rt1[id]=rt1[id-1];rt2[id]=rt2[id-1];
for(int y=0;x;y=x,x=fa[x]){
splay(x);
if(L[x] && last[x]){
int l=last[x]-L[x]+1,r=last[x]-L[fa[x]];
if (l>1) rt1[id]=tr1.update(rt1[id],1,len,1,l-1,r-l+1);
rt2[id]=tr2.update(rt2[id],1,len,l,r,r);
}
if(rs[x]) last[rs[x]]=last[x];
rs[x]=y;
last[y]=0;last[x]=id;
}
}
inline void link(int x,int y){splay(x);fa[x]=y;}
inline void cut(int x){splay(x);last[ls[x]]=last[x];fa[ls[x]]=fa[x];ls[x]=0;}
}
using namespace LCT;
namespace SAM{
int fa[N<<1],ch[N<<1][26],last=1,tot=1;
inline void inc(int c,int id){
int p=last,np=++tot;L[np]=L[p]+1;last=np;
for(;p&& !ch[p][c];p=fa[p]) ch[p][c]=np;
if(!p){fa[np]=1;LCT::link(np,1);}
else{
int q=ch[p][c];
if(L[q]==L[p]+1){fa[np]=q;LCT::link(np,q);}
else{
int nq=++tot;L[nq]=L[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
LCT::cut(q);LCT::copy(nq,q);
fa[nq]=fa[q];LCT::link(nq,fa[nq]);
fa[q]=fa[np]=nq;LCT::link(q,nq);LCT::link(np,nq);
for(;p && ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
LCT::access(np,id);
}
}
char str[5];
int main(){
int t=read();
scanf("%s",s+1);int m=read();
int l=strlen(s+1);len=l+m;
for(int i=1;i<=l;i++) SAM::inc(s[i]-'a',i);
ll ans=0;
for(int i=1;i<=m;i++){
int op=read();
if(op==1){
scanf("%s",str);
str[0]=(str[0]-'a'+ans*t)%26+'a';
SAM::inc(str[0]-'a',++l);
}
else{
int x=read(),y=read();
x=(x-1+ans*t)%l+1;y=(y-1+ans*t)%l+1;
pi ans1=tr1.query(rt1[y],1,len,x),ans2=tr2.query(rt2[y],1,len,x);
ans=(ll)(y-x+2)*(y-x+1)/2-ans1.fi-ans2.fi+(ll)(x-1)*ans2.se;
cout<<ans<<"\n";
}
}
return 0;
}