【ZJOI2020】传统艺能【矩阵快速幂】【DP】

Description

给定一棵 \([1,n]\) 范围内的广义线段树,\(k\) 次等概率随机选择一个区间 \([l,r]\) 执行区间覆盖操作,问最终 \(tag=1\) 的节点数量的期望。

\(n\le 200000,k\le 10^9\)

Solution

根据期望的线性性,可以先求出每个节点最终 \(tag=1\) 的概率,加起来即为答案。

考虑每一个区间覆盖对当前节点 \(tag\) 的影响,因为祖先节点可能会下传 \(tag\),因此也要考虑祖先节点的状态。可以将状态分为:

  • 当前节点和祖先都没有标记
  • 祖先节点有标记,当前节点没有标记
  • 当前节点有标记

\(3\) 种状态。

不妨设父亲节点 \(f\) 的区间为 \([L,R]\),当前节点 \(p\) 的区间为 \([l,r]\),询问区间为 \([ql,qr]\)

  • 修改与 \([L,R]\) 无交,即 \(1\le ql\le qr<L\)\(R<ql\le qr\le n\):此时对状态无影响。

  • 修改包含 \(f\),并在某个 \(u\) 的祖先处打标记终止,即 \(ql\in [1,L],qr\in [R,n]\),此时会导致祖先节点带上标记。

  • 修改包含 \(u\),在 \(u\) 上打标记终止,此时 \(ql\in [1,l],qr\in [r,n]\) 且不属于上一种情况。此时会导致自己带上标记,并且会让祖先节点的标记全部下传。

  • 修改进入了 \(f\),但下一步只进入 \(f\) 的另一个儿子而不进入 \(u\),即 \(1\le ql\le L\le qr< l\) ,或 \(r<ql\le R\le r\le n\):此时祖先节点的标记会下传。

  • 修改与 \(u\) 相交,进入 \(u\) 后继续向下递归:此时祖先节点与当前节点的标记都会清零。

分类讨论这 \(5\) 种情况,设 \(f_{i,0/1/2}\) 表示考虑了前 \(i\) 次修改,当前状态为 \(0/1/2\) 的概率,不难列出转移方程,然后矩阵快速幂优化即可做到 \(\mathcal O(3^3n\log k)\).

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=4e5+10,mod=998244353;
int n,k,m[N],tot,L[N],R[N],fa[N],p[N],q[N],g[N],r[N];
inline void dfs(int l,int r,int f){
	int p=++tot;L[p]=l;R[p]=r;fa[p]=f;
	if(L[p]!=R[p]){
		scanf("%d",&m[p]);
		dfs(l,m[p],p);dfs(m[p]+1,r,p);
	}
}
inline int add(int x,int y){return (x+y>=mod)?x+y-mod:x+y;}
inline int dec(int x,int y){return (x-y<0)?x-y+mod:x-y;}
inline int ksm(int x,int y){
	int ret=1;
	for(;y;y>>=1,x=1ll*x*x%mod) if(y&1) ret=1ll*ret*x%mod;
	return ret;
} 
struct matrix{
	int c[3][3];
	inline void init(int d){
		for(int i=0;i<3;++i) for(int j=0;j<3;++j) c[i][j]=i==j?d:0;
	}
	inline matrix operator *(const matrix &A){
		matrix ret;
		for(int i=0;i<3;++i){
			for(int j=0;j<3;++j){
				ll tmp=0;
				for(int k=0;k<3;++k) tmp+=1ll*c[i][k]*A.c[k][j];
				ret.c[i][j]=tmp%mod;
			}
		}
		return ret;
	}
	inline matrix operator ^(int k){
		matrix ret,A=*this;ret.init(1);
		for(;k;k>>=1,A=A*A) if(k&1) ret=ret*A;
		return ret;
	}
};
inline int S(int x){return (1ll*x*(x+1)/2)%mod;}
int main(){
	scanf("%d%d",&n,&k);
	dfs(1,n,0);
	int al=S(n),all=ksm(S(n),mod-2),ans=0;
	for(int i=1;i<=tot;++i){
		int l=L[i],r=R[i],fl=L[fa[i]],fr=R[fa[i]];
		int p1=add(S(fl-1),S(n-fr));//与父亲无交 
		int p2=1ll*fl*(n-fr+1)%mod;//标记祖先 
		int p3=dec(1ll*l*(n-r+1)%mod,p2);//标记自己
		int p4=add(dec(S(l-1),S(fl-1)),dec(S(n-r),S(n-fr)));//下传父亲,不进入自己 
		int p5=dec(al,add(add(p1,p2),add(p3,p4)));//与自己交但不包含 
		matrix A;A.init(0);
		A.c[0][0]=1ll*add(p1,add(p4,p5))*all%mod;A.c[0][1]=1ll*p5*all%mod;A.c[0][2]=1ll*p5*all%mod;
		A.c[1][0]=1ll*p2*all%mod;A.c[1][1]=1ll*add(p1,p2)*all%mod;
		A.c[2][0]=1ll*p3*all%mod;A.c[2][1]=1ll*add(p3,p4)*all%mod;A.c[2][2]=1ll*dec(al,p5)*all%mod;
		A=A^k;
		ans=add(ans,A.c[2][0]);
	}
	printf("%d\n",ans);
	return 0;
}

posted @ 2021-06-14 20:28  cjTQX  阅读(88)  评论(0编辑  收藏  举报