「PKUWC2018」随机游走

题面在这里!

显然你如果直接带一个子集到树上dp的话复杂度是会炸上天的23333.

考虑期望也是可以进行min_max容斥的,也就是: max{S} = ∑ min{T} * (-1) ^( |T|+1 ) ,其中T是S的一个非空子集,max{S}和min{S}分别代表集合中所有条件都被满足的期望时间 和 集合中至少有一个条件被满足的期望时间, 当然对本题来说就是 所有钦定的点都被到过一次的期望时间 和 第一次到某个钦定的点的期望时间。。。。

发现min非常的好算,对于每个集合直接一次树上dp O(n * log 998244351) 就可以算出来了(千万不要用高斯消元。。。这种树上路径期望问题可以把每个节点的dp值表示 a * f(fa) + b的形式,dfs的时候直接算就行了)

然后把每个集合的min{}算出来之后就可以用类FMT的逆推方法推出每个集合的答案。

不过两者之间不同的是,FMT的式子是: f ‘(S‘) = ∑ f(S) * (-1) ^ ( |S‘| - |S|) ,而这个的是 f ‘(S‘) = ∑ f(S) * (-1) ^ ( |S|+1 )。

但其实并没有什么难度,因为我们把 S‘ 中1的个数按奇偶性讨论一下就可以直接在FMT之后的数组上修改成我们要的样子了。。。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define pb push_back
const int ha=998244353,N=300005;

inline int read(){
	int x=0; char ch=getchar();
	for(;!isdigit(ch);ch=getchar());
	for(;isdigit(ch);ch=getchar()) x=x*10+ch-‘0‘;
	return x-1;
}

inline int add(int x,int y){ x+=y; return x>=ha?x-ha:x;}
inline void ADD(int &x,int y){ x+=y; if(x>=ha) x-=ha;}

int ksm(int x,int y){
	int an=1;
	for(;y;y>>=1,x=x*(ll)x%ha) if(y&1) an=an*(ll)x%ha;
	return an;
}

vector<int> g[23];
int n,Q,root,ans[N],a[23],b[23],S,d[23];
bool bt[N];

void dfs(int x,int fa){
	a[x]=d[x],b[x]=1; int tmp=1;

	for(int i:g[x]) if(i!=fa){
		dfs(i,x),ADD(tmp,ha-a[i]*(ll)d[x]%ha);
		ADD(b[x],b[i]*(ll)d[x]%ha);
	}

	tmp=ksm(tmp,ha-2);
	a[x]=a[x]*(ll)tmp%ha;
	b[x]=b[x]*(ll)tmp%ha;

	if((1<<x)&S) a[x]=b[x]=0;
}

inline void solve(){
	bt[0]=1; for(int i=1;i<(1<<n);i++) bt[i]=!bt[i^(i&-i)];

	for(int i=0;i<n;i++)
	    for(int j=(1<<n)-1;j>=0;j--) if(!((1<<i)&j)) ADD(ans[j|(1<<i)],ha-ans[j]);

	for(int i=(1<<n)-1;i>=0;i--) if(bt[i]) ans[i]=ha-ans[i];

    for(int k,s;Q;Q--){
    	scanf("%d",&k),s=0;
    	while(k--) s|=1<<read();
    	printf("%d\n",ans[s]);
	}
}

int main(){
	scanf("%d%d",&n,&Q),root=read();
	int uu,vv;
	for(int i=1;i<n;i++){
		uu=read(),vv=read();
		g[uu].pb(vv),g[vv].pb(uu);
		d[uu]++,d[vv]++;
	}

	for(int i=0;i<n;i++) d[i]=ksm(d[i],ha-2);

	for(S=1;S<(1<<n);S++){
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));

		dfs(root,-1),ans[S]=b[root];
	}

	solve();

	return 0;
}

  

原文地址:https://www.cnblogs.com/JYYHH/p/9194873.html

时间: 2024-08-30 14:56:04

「PKUWC2018」随机游走的相关文章

Loj #2542. 「PKUWC2018」随机游走

Loj #2542. 「PKUWC2018」随机游走 题目描述 给定一棵 \(n\) 个结点的树,你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 \(Q\) 次询问,每次询问给定一个集合 \(S\),求如果从 \(x\) 出发一直随机游走,直到点集 \(S\) 中所有点都至少经过一次的话,期望游走几步. 特别地,点 \(x\)(即起点)视为一开始就被经过了一次. 答案对 $998244353 $ 取模. 输入格式 第一行三个正整数 \(n,Q,x\). 接下来 \(

「Luogu4321」随机游走

「Luogu4321」随机游走 题目描述 有一张 \(n\) 个点 \(m\) 条边的无向图,\(Q\) 组询问,每次询问给出一个出发点和一个点集 \(S\) ,求从出发点出发随机游走走遍这个点集的期望步数. \(1 \leq n \leq 18, 1 \leq Q \leq 10^5\) 解题思路 : 听说是 \(\text{pkuwc2018d2t3}\) 加强版?但是原题时限是1s,各种卡不进去感觉一定要写 \(\text{Min-Max}\) 容斥,不过反正我今年听指导建议没报 \(\t

「PKUWC2018」随机算法

传送门 Description 我们知道,求任意图的最大独立集是一类NP完全问题,目前还没有准确的多项式算法,但是有许多多项式复杂度的近似算法. 例如,小 C 常用的一种算法是: 对于一个 \(n\) 个点的无向图,先等概率随机一个 $1\ldots n $的排列 \(p[1\ldots n]\). 维护答案集合 \(S\),一开始 \(S\) 为空集,之后按照 \(i=1\ldots n\) 的顺序,检查 \(\{p[i]\}\cup S\) 是否是一个独立集,如果是的话就令 \(S=\{p[

loj#2540. 「PKUWC2018」随机算法

传送门 完了pkuwc咋全是dp怕是要爆零了-- 设\(f(S)\)表示\(S\)的排列数,\(S\)为不能再选的点集(也就是选到独立集里的点和与他们相邻的点),\(mx(S)\)表示\(S\)状态下对应的独立集大小,枚举点\(i\),如果\(i\)不在\(S\)里,分情况考虑,设\(w[i]\)表示点\(i\)以及与之相邻的点,\(T=S|w[i]\),\(sz[S]\)表示二进制\(S\)有多少个\(1\),如果\(mx[T]=mx[S]+1\),那么\[f[T]+=f[S]\times A

随机游走

在python中,可以利用数组操作来模拟随机游走. 下面是一个单一的200步随机游走的例子,从0开始,步长为1和-1,且以相等的概率出现.纯Python方式实现,使用了内建的 random 模块: # 随机游走 import matplotlib.pyplot as plt import random position = 0 walk = [position] steps = 200 for i in range(steps): step = 1 if random.randint(0, 1)

随机游走的matlab实现

<span style="font-family:KaiTi_GB2312;font-size:14px;">%随机游走产生图像效果实现,随机游走类似布朗运动,就是随机的向各个方向走</span> <span style="font-family:KaiTi_GB2312;font-size:14px;"><span style="color: rgb(68, 68, 68); line-height: 21px;

bzoj 3143 随机游走

题意: 给一个简单无向图,一个人从1号节点开始随机游走(即以相同概率走向与它相邻的点),走到n便停止,问每条边期望走的步数. 首先求出每个点期望走到的次数,每条边自然是从它的两个端点走来. 1 /************************************************************** 2 Problem: 3143 3 User: idy002 4 Language: C++ 5 Result: Accepted 6 Time:736 ms 7 Memory:

[PKUWC 2018]随机游走

Description 题库链接 给定一棵 \(n\) 个结点的树,你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 \(Q\) 次询问,每次询问给定一个集合 \(S\) ,求如果从 \(x\) 出发一直随机游走,直到点集 \(S\) 中所有点都至少经过一次的话,期望游走几步. 特别地,点 \(x\)(即起点)视为一开始就被经过了一次. 答案对 \(998244353\) 取模. Solution 不妨设 \(f_{i,S}\) 表示在点 \(i\) 时,要遍历集合

随机游走模型(RandomWalk Mobility)

随机游走模型由首先由爱因斯坦在1926年以数学方式描述.由于自然界中的许多实体会以不可预知的方式移动,因此随机游走模型用来描述这种不稳定的移动.在这种移动模型中,移动节点随机选择一个方向和速度来从当前位置移动到新的位置.新的速度和方向分别从预定义的范围[speedmin,speedmax]和[0,2].移动节点的每次移动会以恒定的时间间隔t或恒定的行进距离d进行,结束后会计算新的方向和速度.如果此模型的移动节点到达模拟边界,则它将从模拟边界"弹回",其角度有入射方向确定,然后沿着这条路