zoukankan      html  css  js  c++  java
  • 「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
    ",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;
    } 
    

      

  • 相关阅读:
    《多处理器编程的艺术》读书笔记(2) 互斥
    《多处理器编程的艺术》读书笔记(7) CLH队列锁
    rdlc 套打实现
    《多处理器编程的艺术》读书笔记(6) 队列锁
    《多处理器编程的艺术》读书笔记(3) 双线程解决方案
    《多处理器编程的艺术》读书笔记(4) 自旋锁(1)
    《多处理器编程的艺术》读书笔记(1) 并行的困境和加速比
    反色,霓虹灯,浮雕
    《多处理器编程的艺术》读书笔记(5) 自旋锁(2)
    Open ESRI shape files in Quantum GIS Anny
  • 原文地址:https://www.cnblogs.com/JYYHH/p/9194873.html
Copyright © 2011-2022 走看看