[ZJOI2020]传统艺能

https://www.luogu.com.cn/blog/Thinking/solution-p6630
把节点分为五类
1.不会进入o的父亲且不在祖先终止(与o父亲区间不交)
2.在o打标记终止(包含o但不能包含o的父亲)
3.标记了o的祖先(包含o父亲)
4.下推了o父亲标记但没进入o(与o兄弟有交但与o不交)
5.进入o

记录(f_i)表示(i)有标记概率 (g_i)表示(i)祖先(含(i))有标记概率 矩阵快速幂

#include<bits/stdc++.h>
using namespace std;
#define fp(i,l,r) for(register int (i)=(l);i<=(r);++(i))
#define fd(i,l,r) for(register int (i)=(l);i>=(r);--(i))
#define fe(i,u) for(register int (i)=front[(u)];(i);(i)=e[(i)].next)
#define mem(a) memset((a),0,sizeof (a))
#define O(x) cerr<<#x<<':'<<x<<endl
#define int long long
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
	return x*f;
}
void wr(int x){
	if(x<0)putchar('-'),x=-x;
	if(x>=10)wr(x/10);
	putchar('0'+x%10);
}
const int mod=998244353;
inline void tmod(int &x){x%=mod;}
inline int qpow(int a,int b){
	int res=1;a%=mod;
	for(;b;b>>=1,tmod(a*=a))
		if(b&1)tmod(res*=a);
	return res;
}
inline int ginv(int x){return qpow(x,mod-2);}
struct Mat{
	int a11,a12,a13,a22,a23;
	inline Mat operator*(const Mat &b)const{
		return {a11*b.a11%mod,(a11*b.a12+a12*b.a22)%mod,(a11*b.a13+a12*b.a23+a13)%mod,
				a22*b.a22%mod,(a22*b.a23+a23)%mod};
	}
};
int ans,inv,n,K;
inline Mat qpow(Mat a,int b){
	Mat res={1,0,0,1,0};
	for(;b;b>>=1,a=a*a)
		if(b&1)res=res*a;
	return res;
}
void solve(int L,int R,int l,int r){
	if(L<R){
		int mid=read();
		solve(L,mid,L,R);solve(mid+1,R,L,R);
	}
	if(!l)tmod(ans+=inv);
	else{
		int a=(l*(l-1)+(n-r)*(n-r+1)>>1)%mod,
			b=(L*(r-R)+(n-R+1)*(L-l))%mod,
			c=l*(n-r+1)%mod,
			d=((l+L-1)*(L-l)+(n+n-R-r+1)*(r-R)>>1)%mod;
		tmod(ans+=qpow({(a+c)*inv%mod,d*inv%mod,b*inv%mod,(a+d)*inv%mod,(b+c)*inv%mod},K).a13);
	}
}
main(){
	n=read();K=read();inv=ginv(n*(n+1)/2);
	solve(1,n,0,0);
	printf("%lld
",ans);
	return 0;
}

原文地址:https://www.cnblogs.com/WinterSpell/p/13274201.html