【LOJ 2542】【PKUWC2018】 随机游走(最值反演 + 树上期望dp)

哇我太菜啦555555

不妨钦定我们需要访问的点集为S,在S已知的情况下,我们令f(x)表示从x走到点集S中任意一点的期望步数。

xS,则显然f(x)=0,否则f[x]=1d[x]f[ch[x]]+1。其中d[x]表示与x相连的节点个数,ch[x]为与x相连的节点。

然后就列出了n条式子,显然是一个n元一次方程,可以考虑用高斯消元去求解,这样时间复杂度是O(n32n),只能拿60分(然而我考场上是零分啊呜呜呜)

我们考虑用些快速点的方法,考虑将f[x]化为Axf[fa[x]]+Bx。其中fa[x]表示x的父亲。

f[x]=Ax[fa[x]]+Bx=1d[x]f[ch[x]]

f[x]=1d[x]f[fa[x]]+1d[x](Ach[x]f[x]+Bch[x])+1

经过化简后,得

f[x]=f[fa[x]]+Bch[x]+1d[u]Ach[x]

我们令g[S]表示从给定起点X出发,走到集合S中任意一个点的期望步数。

那么显然,g[S]=f[X]。求出所有状态的期望的时间复杂度显然为O(n2n)

 

我们令G[S]表示从给定起点X出发,将集合S中每个点至少走一次的期望步数。

根据minmax容斥的相关内容,有

G[S]=iSg[i]×(1)|i|+1

然后我们可以花O(3n)枚举子集,预处理出所有答案。

查询的时候O(1)查询即可。

完结撒花

 

复制代码
 1 #include<bits/stdc++.h>
 2 #define M 18
 3 #define MOD 998244353
 4 #define L long long
 5 using namespace std;
 6 
 7 L pow_mod(L x,L k){
 8     L ans=1;
 9     while(k){
10         if(k&1) ans=ans*x%MOD; 
11         x=x*x%MOD; k>>=1;
12     }
13     return ans;
14 }
15 
16 L d[M]={0},invd[M]={0};    
17 struct edge{int u,next;}e[M<<1]={0}; int head[M]={0},use=0;
18 void add(int x,int y){use++;e[use].u=y;e[use].next=head[x];head[x]=use;}
19 
20 L f[1<<M]={0},ans[1<<M]={0},zf[1<<M]={0},a[M]={0},b[M]={0}; int ok[1<<M]={0};
21 
22 int n,q,rt;
23 void dfs(int x,int fa,int S){
24     if((1<<x)&S) return;
25     for(int i=head[x];i;i=e[i].next)
26     if(e[i].u!=fa){
27         dfs(e[i].u,x,S);
28         b[x]+=b[e[i].u];
29         a[x]+=a[e[i].u];
30     }
31     b[x]%=MOD; a[x]%=MOD;
32     L inv=pow_mod((d[x]-a[x]+MOD)%MOD,MOD-2);
33     a[x]=inv; 
34     b[x]=(b[x]*inv+inv*d[x])%MOD;
35 }
36 
37 void solve(int x){
38     ok[x]=1;
39     for(int i=x;i;i=x&(i-1))
40     ans[x]+=zf[i]*f[i];
41     ans[x]=(ans[x]%MOD+MOD)%MOD;
42 }
43 
44 int main(){
45     //freopen("a.out","w",stdout);
46     scanf("%d%d%d",&n,&q,&rt); rt--;
47     for(int i=1;i<n;i++){
48         int x,y; scanf("%d%d",&x,&y);
49         x--; y--; add(x,y); add(y,x);
50         d[x]++; d[y]++;
51     }
52     for(int i=0;i<n;i++) invd[i]=pow_mod(d[i],MOD-2);
53     int hh=1<<n;
54     for(int i=1;i<hh;i++){
55         memset(a,0,sizeof(a)); 
56         memset(b,0,sizeof(b));
57         dfs(rt,-1,i);
58         f[i]=b[rt]; zf[i]=-1;
59         for(int j=0;j<n;j++)
60         if((1<<j)&i) zf[i]=-zf[i];
61     }
62     while(q--){
63         int k,hh=0; scanf("%d",&k);
64         while(k--){
65             int x; scanf("%d",&x); 
66             hh+=1<<(x-1);
67         }
68         if(!ok[hh]) solve(hh);
69         printf("%lld\n",ans[hh]);
70     }
71 }
复制代码

 

posted @   AlphaInf  阅读(393)  评论(2编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示