Loj #2542. 「PKUWC2018」随机游走

Loj #2542. 「PKUWC2018」随机游走

题目描述

给定一棵 \(n\) 个结点的树,你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去。

有 \(Q\) 次询问,每次询问给定一个集合 \(S\),求如果从 \(x\) 出发一直随机游走,直到点集 \(S\) 中所有点都至少经过一次的话,期望游走几步。

特别地,点 \(x\)(即起点)视为一开始就被经过了一次。

答案对 $998244353 $ 取模。

输入格式

第一行三个正整数 \(n,Q,x\)。

接下来 \(n-1\) 行,每行两个正整数 \((u,v)\) 描述一条树边。

接下来 \(Q\) 行,每行第一个数 \(k\) 表示集合大小,接下来 \(k\) 个互不相同的数表示集合 \(S\)。

输出格式

输出 \(Q\) 行,每行一个非负整数表示答案。

数据范围与提示

对于 \(20\%\) 的数据,有 \(1\leq n,Q\leq 5\)。

另有 \(10\%\) 的数据,满足给定的树是一条链。

另有 \(10\%\) 的数据,满足对于所有询问有 \(k=1\)。

另有 \(30\%\) 的数据,满足 \(1\leq n\leq 10 ,Q=1\)。

对于 \(100\%\) 的数据,有 \(1\leq n\leq 18\),\(1\leq Q\leq 5000\),\(1\leq k\leq n\)。

Orz

首先根据\(\min-\max\) 反演我们知道:
\[
\max(S)=\sum_{T\subseteq S}(-1)^{|T|-1}\min(T)
\]
设\(f_{v,S}\)表示从\(v\)出发,经过\(S\)中至少一个节点的期望步数。

如果\(v\in S\),\(f_{v,S}=0\),否则:
\[
f_v=1+\frac{1}{d_v}f_{fa}+\frac{1}{d_v}\sum f(u)\\]
然后这是颗树,我们可以将\(DP\)方程移项变成只与\(fa\)的\(f\)值个一个常数有关。

设:
\[
f(v)=A_v*f_{fa}+B_v\\]
带回去化简:
\[
f_v=1+\frac{1}{d_v}f_{fa}+\frac{1}{d_v}\sum (A_u*f_v+B_u)\(d_v-\sum A_u)*f_v=d_v+f_{fa}+\sum B_u\f_v=\frac{1}{d_v-\sum A_u}*f_{fa}+\frac{d_v+\sum B_u}{d_v-\sum A_u}
\]
得到:
\[
A_v=\frac{1}{d_v-\sum A_u},B_v=\frac{d_v+\sum B_u}{d_v-\sum A_u}
\]
代码:

#include<bits/stdc++.h>
#define ll long long
#define N 19

using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}

const ll mod=998244353;
ll ksm(ll t,ll x) {
    ll ans=1;
    for(;x;x>>=1,t=t*t%mod)
        if(x&1) ans=ans*t%mod;
    return ans;
}

int n;
int X,m;
struct road {int to,nxt;}s[N<<1];
int h[N],cnt;
void add(int i,int j) {s[++cnt]=(road) {j,h[i]};h[i]=cnt;}

ll w[N];
int d[N];
ll A[N],B[N],f[N];
int tag[N];

void dfs(int v,int fa) {
    A[v]=B[v]=0;
    ll sumA=0,sumB=0;
    for(int i=h[v];i;i=s[i].nxt) {
        int to=s[i].to;
        if(to==fa) continue ;
        dfs(to,v);
        sumA=(sumA+A[to])%mod;
        sumB=(sumB+B[to])%mod;
    }
    if(tag[v]) A[v]=B[v]=0;
    else A[v]=ksm(d[v]-sumA+mod,mod-2),B[v]=(d[v]+sumB)*ksm(d[v]-sumA+mod,mod-2)%mod;
}

void dfs2(int v,int fa) {
    f[v]=(A[v]*f[fa]+B[v])%mod;
    for(int i=h[v];i;i=s[i].nxt) {
        int to=s[i].to;
        if(to==fa) continue ;
        dfs2(to,v);
    }
}
int mn[1<<N];
int main() {
    n=Get(),m=Get(),X=Get();
    int a,b;
    for(int i=1;i<n;i++) {
        a=Get(),b=Get();
        add(a,b),add(b,a);
        d[a]++,d[b]++;
    }
    for(int S=1;S<1<<n;S++) {
        for(int i=1;i<=n;i++) if(S>>i-1&1) tag[i]=1;
        dfs(1,0),dfs2(1,0);
        mn[S]=f[X];
        for(int i=1;i<=n;i++) if(S>>i-1&1) tag[i]=0;
    }
    for(int S=1;S<1<<n;S++) {
        int cnt=0;
        for(int i=0;i<n;i++) cnt+=S>>i&1;
        if(!(cnt&1)) mn[S]=(mod-mn[S])%mod;
    }
    for(int i=0;i<n;i++) {
        for(int S=1;S<1<<n;S++) {
            if(S>>i&1) mn[S]=(mn[S]+mn[S^(1<<i)]+mod)%mod;
        }
    }
    while(m--) {
        int k=Get();
        int sta=0;
        while(k--) sta|=1<<Get()-1;
        cout<<mn[sta]<<"\n";
    }
    return 0;
}

原文地址:https://www.cnblogs.com/hchhch233/p/10763995.html

时间: 2024-08-02 06:30:10

Loj #2542. 「PKUWC2018」随机游走的相关文章

「PKUWC2018」随机游走

题面在这里! 显然你如果直接带一个子集到树上dp的话复杂度是会炸上天的23333. 考虑期望也是可以进行min_max容斥的,也就是: max{S} = ∑ min{T} * (-1) ^( |T|+1 ) ,其中T是S的一个非空子集,max{S}和min{S}分别代表集合中所有条件都被满足的期望时间 和 集合中至少有一个条件被满足的期望时间, 当然对本题来说就是 所有钦定的点都被到过一次的期望时间 和 第一次到某个钦定的点的期望时间.... 发现min非常的好算,对于每个集合直接一次树上dp

「Luogu4321」随机游走

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

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

Loj #2541「PKUWC2018」猎人杀

Loj #2541. 「PKUWC2018」猎人杀 题目链接 好巧妙的题! 游戏过程中,概率的分母一直在变化,所以就非常的不可做. 所以我们将问题转化一下:我们可以重复选择相同的猎人,只不过在一个猎人被选择了过后我们就给他打上标记,再次选择他的时候就无效.这样与原问题是等价的. 证明: 设\(sum=\sum_iw_i,kill=\sum_{i被杀死了}w_i\). 攻击到未被杀死的猎人\(i\)的概率为\(P\). 则根据题意\(P=\frac{w_i}{sum-kill}\). 问题转化后:

「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#2537. 「PKUWC2018」Minimax

传送门 感觉我去pkuwc好像只有爆零的份-- 设\(f_{u,i}\)表示\(u\)取到\(i\)的概率,那么有如下转移 \[f_{u,i}=f_{ls,i}(p_u\sum_{j<i}f_{rs,j}+(1-p_u)\sum_{j>i}f_{rs,j})+\\f_{rs,i}(p_u\sum_{j<i}f_{ls,j}+(1-p_u)\sum_{j>i}f_{ls,j})\] 然后用线段树合并即可,最后在根节点的线段树上\(dfs\)统计答案 //minamoto #inclu

随机游走

在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: