zoukankan      html  css  js  c++  java
  • LOJ2542 随机游走 Min-Max容斥+树上期望DP

    搞了一下午 真的是啥都不会
    首先这道题要用到Min-Max容斥 得到的结论是

    (Max(S))表示集合里最晚被访问的节点被访问的期望步数
    (Min(S))表示集合里最早被访问的节点被访问的期望步数
    那么$ Max(S) = ∑_{T in S} {-1^ { lvert T vert+1} }Min(T)$
    (这个相关的证明和理解可以看看HDU4336 附一个题解)

    考虑对于一个集合(S)如何计算(Min(S))
    (d_u)为点(u)的度数
    (u otin S spacespace Rightarrow space spacedisplaystyle f_u=f_{fa[u]}+1+sum (f_{son[u]}+1) imes frac{1}{d_u})
    (u in Sspacespace Rightarrow space space f(u)=0)
    对于树上的期望可以写成$f_u=k_u imes f_{fa[u]}+b_u (的形式 于是)sum f_{son[u]}=sum_{fa[v]=u}(a_v imes f_u+b_v)$
    代入之前的式子并化简得
    (displaystyle (1-frac{sum A_v}{d_u}) f(u) = frac{1}{d_u}f_{mathrm{fa}[u]}+(1+frac{B_v}{d_u}))
    这个(dfs)一遍就可以维护所有点的(a,b)

    考虑如何回答询问
    可以对于每个询问的集合(S)暴力枚举子集 这样是可以过得
    但我们也可以像类似(FMT)的做法先维护出所有集合的子集之和再(O(1))回答每个询问 这里注意每个集合初值的正负

    #include<bits/stdc++.h>
    using namespace std;
    #define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
    #define pa pair<int,int>
    #define mod 998244353
    #define ll long long
    #define mk make_pair
    #define pb push_back
    #define lb double
    #define fi first
    #define se second
    #define cl(x) memset(x,0,sizeof x)
    #ifdef Devil_Gary
    #define bug(x) cout<<(#x)<<" "<<(x)<<endl
    #define debug(...) fprintf(stderr, __VA_ARGS__)
    #else
    #define bug(x)
    #define debug(...)
    #endif
    const int INF = 0x7fffffff;
    const int N=1e6+5;
    const int M=25;
    /*
    char *TT,*mo,but[(1<<15)+2];
    #define getchar() ((TT==mo&&(mo=(TT=but)+fread(but,1,1<<15,stdin),TT==mo))?-1:*TT++)//*/
    inline int read(){
        int x=0,rev=0,ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')rev=1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
        return rev?-x:x;
    }
    struct Edge{
    	int v,nxt;
    }e[N<<1];
    int n,Q,rt,tot,head[M],bin[N],a[M],b[M],bit[N],d[M],f[N];
    void add(int u,int v){
    	e[++tot].v=v,e[tot].nxt=head[u],head[u]=tot,++d[u];
    	e[++tot].v=u,e[tot].nxt=head[v],head[v]=tot,++d[v];
    }
    int poww(int x,int y){
    	int ans=1;
    	while(y){
    		if(y&1) ans=(ll)ans*x%mod;
    		y>>=1,x=(ll)x*x%mod;
    	}
    	return ans;
    }
    void dfs(int x,int fa,int S){
    	a[x]=b[x]=0;
    	if((1<<x)&S) return;
    	for(int i=head[x];i;i=e[i].nxt){
    		int j=e[i].v;
    		if(j==fa) continue;
    		dfs(j,x,S);
    		(a[x]+=a[j])%=mod,(b[x]+=b[j])%=mod;
    	}
    	int tmp=poww((1+mod-(ll)a[x]*d[x]%mod)%mod,mod-2);
    	a[x]=(ll)tmp*d[x]%mod,b[x]=(ll)(1+(ll)b[x]*d[x]%mod)*tmp%mod;
    //	cout<<x<<" "<<a[x]<<" "<<b[x]<<endl;
    }
    int main(){
    #ifdef Devil_Gary
    	freopen("in.txt","r",stdin);
    #endif
    	n=read(),Q=read(),rt=read()-1;
    	for(int i=1;i<n;i++) add(read()-1,read()-1);
    	for(int i=0;i<n;i++) d[i]=poww(d[i],mod-2);
        for(int i=1;i<(1<<n);i++) bin[i]=bin[i>>1]+(i&1);
    	for(int i=0;i<(1<<n);i++) dfs(rt,-1,i),f[i]=bin[i]&1?b[rt]:(mod-b[rt])%mod;
    //	for(int i=0;i<(1<<n);i++) cout<<i<<" "<<f[i]<<endl;
    	for(int j=0;j<n;j++) for(int i=0;i<(1<<n);i++)  if(i&(1<<j)) (f[i]+=f[i^(1<<j)])%=mod;
    	while(Q--){
    		int S=0;
    		for(int T=read();T;T--) S|=(1<<(read()-1)); 
    //		bug(S);
    		printf("%d
    ",f[S]);
    	}
    }
    
    
  • 相关阅读:
    Java必会之多线程
    第一周JVM核心技术-工具与GC策略
    JVM核心技术(第一篇)
    SpringCloudAlibaba使用seata做分布式事务
    Go中的interface(接口)
    快排与堆排
    优先队列原来不难!带你手写一个
    【LeetCode】557. 反转字符串中的单词 III
    【LeetCode】214. 最短回文串
    【LeetCode】17. 电话号码的字母组合(回溯)
  • 原文地址:https://www.cnblogs.com/devil-gary/p/9216631.html
Copyright © 2011-2022 走看看