T1 4019: 白玉楼前(youmu)
题目描述
妖梦现在要玩幽幽子的游戏,她才能拿回自己的半灵。
游戏规则是这样的:
幽幽子有 n 个点,现在她让妖梦对每个点随机一条出边(随机到每个点的概率都相等),然后得到一张图。(注意:可以自环)
如果这张图任意一个点沿着边走两步(显然这样的走法唯一)都能到达自身,则幽幽子可以通关。
现在幽幽子想问妖梦,她通关的概率是多少?
两个图不同,当且仅当存在一条边出现在图 A 中且不出现在图 B 中。图中的点有编号,边无编号。答案 mod 998244353。
题解
神犇都想到题解了(所以我没想到)
考虑dp,设表示前i个点已经匹配完的方案数
则
预处理f数组即可,最后乘
具体见代码
#include <bits/stdc++.h>
#define E register
using namespace std;
const int P=998244353,N=5e5+5;
int T,n,jc[N],ny[N],f[N],ans[N];
inline int K(E int x,E int y){
int A=1;while(y){
if (y&1) A=1ll*A*x%P;
x=1ll*x*x%P;y>>=1;
}
return A;
}
int main(){
f[0]=f[1]=1;
for (int i=2;i<N;i++)
f[i]=(f[i-1]+1ll*(i-1)*f[i-2]%P)%P;
for (scanf("%d",&T);T--;)
scanf("%d",&n),
printf("%d
",1ll*K(K(n,P-2),n)*f[n]%P);
return 0;
}
T2 4020: 式神守护(yukari)
题目描述
紫妈有n 个隙间排成一列,每个隙间都有一个权值val 。
她可以选出某些隙间来召唤式神:一组隙间能成功召唤式神当且仅当他们的权值和为m的倍数。(可以是0 倍)
现在紫妈试图召唤 Q次式神,每次给出一个l和r ,她试图在第l到r个隙间中召唤式神,她会选择其中一些隙间(不一定需要连续的一些)召唤式神。她想知道,有多少种方案可以成功召唤式神。
题解
考场只想到了线段树,然鹅效率不优秀(只有)
考虑分治
设要处理这一段区间内的答案
我们可以把询问没有跨过mid的递归下去求解,则留在这段区间的询问,左端点在内,右端点在内
考虑
设表示从向左到,余数为的方案数
设表示从向右到,余数为的方案数
可以很快得到dp式子:
所以对于一个跨过的询问,我们可以得到他的答案
效率
具体见代码
#include <bits/stdc++.h>
#define I inline
#define _(d) while(d(isdigit(c=getchar())))
using namespace std;
I int R(){
int x;char c;_(!);x=(c^48);
_()x=(x<<3)+(x<<1)+(c^48);return x;
}
const int N=2e5+5,P=1e9+7;
int n,m,a[N],q,s[N],f[N][20],g[N][20];
struct O{int l,r,d;}p[N],h[N];
void solve(int l,int r,int ql,int qr){
if (l==r){
for (int i=ql;i<=qr;i++)
s[p[i].d]=(a[l]<1)+1;
return;
}
int mid=l+r>>1,cl=ql,cr=qr,kl;
for (int i=ql;i<=qr;i++){
if (p[i].r<=mid) h[cl++]=p[i];
if (p[i].l>mid) h[cr--]=p[i];
}
kl=cl;
for (int i=ql;i<=qr;i++)
if (p[i].l<=mid && p[i].r>mid) h[kl++]=p[i];
for (int i=ql;i<=qr;i++) p[i]=h[i];
if (cl>ql) solve(l,mid,ql,cl-1);
if (cr<qr) solve(mid+1,r,cr+1,qr);
f[mid][0]=1;for (int j=1;j<m;j++) f[mid][j]=0;
g[mid+1][0]=1;for (int j=1;j<m;j++) g[mid+1][j]=0;
f[mid][a[mid]]++;g[mid+1][a[mid+1]]++;
for (int i=mid-1;i>=l;i--) for (int j=0;j<m;j++)
f[i][j]=(f[i+1][j]+f[i+1][(j-a[i]+m)%m])%P;
for (int i=mid+2;i<=r;i++) for (int j=0;j<m;j++)
g[i][j]=(g[i-1][j]+g[i-1][(j-a[i]+m)%m])%P;
for (int i=cl;i<=cr;i++) for (int j=0;j<m;j++)
s[p[i].d]=(s[p[i].d]+1ll*f[p[i].l][j]*g[p[i].r][(m-j)%m]%P)%P;
}
int main(){
n=R();m=R();for (int i=1;i<=n;i++) a[i]=R()%m;
q=R();for (int i=1;i<=q;i++) p[i].l=R(),p[i].r=R(),p[i].d=i;
solve(1,n,1,q);
for (int i=1;i<=q;i++) printf("%d
",s[i]);
return 0;
}
T3 4021: 西行妖下(yuyuko)
题目描述
幽幽子站在西行妖下,她需要解封西行妖最后的力量。
西行妖可以当作一个有n 个点的树,每个点都有一些符文,初始每个点符文个数为1。
幽幽子可以施展魔法,将符文随机移动,来解封封印。每个点上的符文可以看作是一个1~m 的排列,原本的状态为1,2,3,4,……,m 按顺序排列(m 为符文的个数)。想要解封一个点上的封印,要求排列中对于任意的i,pi !=i。幽幽子每次的魔法效果是随机形成一个排列,尝试能否解除封印。
幽幽子每次会走过一条路径,从s 到t,对每个点施展一次魔法,并询问能解封的点的期望个数。
现在有Q 次操作:
Add s t x 在s 到t 的路径上每个点加x 个新的符文。
Multi s t x 在s 到t 的路径上,每个点符文个数*x。
Query s t 求从s 到t 解封点的期望个数是多少。
(注意:每次Query 操作是独立的,即前一次Query 中施展的魔法在Query 结束时被立即撤销,所有走过的路径上点的符文排列变为p i=i,对后续操作不产生影响)
题解
来自百度百科的错排详情
可以把期望值看成错排方案数/排列数
因为错排方案数为
排列数为,所以期望值为
当n很大的时候约等于1/e,所以我们只需要求出当n不超过20时的期望值,超过20的当做20来做,然后可以区间修改啦
其实因为每个数修改得不会超过20,所以我们可以直接单点修改就好了
具体见代码
#include <bits/stdc++.h>
#define Ls k<<1
#define Rs Ls|1
#define db double
#define I inline
#define E register
using namespace std;
const int N=80005;
int tt,t,n,q,id[N],head[N],V[N*2],nex[N*2],fa[N],son[N],sz[N],top[N],dep[N];
struct T{db p;int w;}a[N*4];char str[7];db F[21],f[21],jc[21];
I void add(E int u,E int v){V[++t]=v;nex[t]=head[u];head[u]=t;}
I void dfs1(E int x,E int fat,E int deep){
fa[x]=fat;dep[x]=deep;sz[x]=1;
for (int i=head[x];i;i=nex[i]){
if (V[i]==fat) continue;
dfs1(V[i],x,deep+1);sz[x]+=sz[V[i]];
if (sz[V[i]]>sz[son[x]]) son[x]=V[i];
}
}
I void dfs2(E int x,E int tp){
top[x]=tp;id[x]=++tt;
if (son[x]) dfs2(son[x],tp);
for (E int i=head[x];i;i=nex[i])
if (V[i]^fa[x] && V[i]^son[x])
dfs2(V[i],V[i]);
}
I void build(E int k,E int l,E int r){
a[k].w=1;a[k].p=0.0;if (l==r) return;
int mid=l+r>>1;build(Ls,l,mid);
build(Rs,mid+1,r);
}
I void update(E int k,E int l,E int r,E int L,E int R,E int v,E bool ty){
if (l==r){
if (ty) a[k].w=min(20,a[k].w+v);
else a[k].w=min(20,a[k].w*v);
a[k].p=F[a[k].w];return;
}
int mid=l+r>>1;
if (mid>=L && a[Ls].w<20) update(Ls,l,mid,L,R,v,ty);
if (mid<R && a[Rs].w<20) update(Rs,mid+1,r,L,R,v,ty);
a[k].w=min(a[Ls].w,a[Rs].w);a[k].p=a[Ls].p+a[Rs].p;
}
I void update_chain(E int x,E int y,E bool ty,E int v){
while(top[x]!=top[y]){
if (dep[top[x]]<dep[top[y]]) swap(x,y);
update(1,1,n,id[top[x]],id[x],v,ty);x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
update(1,1,n,id[x],id[y],v,ty);
}
I db query(E int k,E int l,E int r,E int L,E int R){
if (L<=l && r<=R) return a[k].p;
int mid=l+r>>1;
if (mid<L) return query(Rs,mid+1,r,L,R);
if (mid>=R) return query(Ls,l,mid,L,R);
return query(Ls,l,mid,L,R)+query(Rs,mid+1,r,L,R);
}
I void query_chain(E int x,E int y){
db j=0.0;
while(top[x]!=top[y]){
if (dep[top[x]]<dep[top[y]]) swap(x,y);
j=j+query(1,1,n,id[top[x]],id[x]);x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
printf("%.1lf
",j+query(1,1,n,id[x],id[y]));
}
int main(){
f[0]=jc[0]=jc[1]=1.0;f[1]=0.0;
for (int i=2;i<21;i++)
f[i]=1.0*(i-1)*(f[i-1]+f[i-2]),
jc[i]=1.0*i*jc[i-1],F[i]=f[i]/jc[i];
scanf("%d",&n);
for (E int u,v,i=1;i<n;i++)
scanf("%d%d",&u,&v),add(u,v),add(v,u);
dfs1(1,0,1);dfs2(1,1);build(1,1,n);
for (scanf("%d",&q);q--;){
scanf("%s",str);int x,y,z;
if (str[0]=='A'){
scanf("%d%d%d",&x,&y,&z);
if (z) update_chain(x,y,1,z);
}
else if (str[0]=='M'){
scanf("%d%d%d",&x,&y,&z);
if (z>1) update_chain(x,y,0,z);
}
else scanf("%d%d",&x,&y),query_chain(x,y);
}
return 0;
}