zoukankan      html  css  js  c++  java
  • NFLSOJ #10317. -「2020联考北附2」三千世界(找等价表达+树形 dp)

    题面传送门

    出题人可能原本感觉没啥难度的 T2 竟然变成了防 AK 题,奇迹奇迹(

    首先带着这个 (max) 肯定不太好处理,考虑找出 (f(S)) 的等价表达。我们考虑以 (1) 为根 DFS 一遍整棵树,然后考虑贪心。每次贪心地找到所有路径中 LCA 最深的路径,如果这条路径上所有节点都没被访问过我们就将该路径上所有节点都设为被访问过并令答案加一,否则我们直接不管这条路径。感性理解一下可知我们采取这样的策略肯定能取到较多的路径。

    接下来考虑求答案的事。考虑方案数转期望,我们求出每个点有多大概率作为某个被选择路径的 LCA 出现过,然后把它们加起来再乘上 (2^{n^2}) 就是答案。那么怎么求呢?注意到对于一个点如果它的某个祖先被访问了,那么这个点也没有用了。因此我们可以将选择一条路径视作直接将这个子树吃掉(虽然这样说好像有点奇怪?),这样就可以 DP 了,我们设 (dp_{u,j}) 表示钦定了 LCA 在 (u) 子树内的路径,还有 (j) 个点没有被吃掉的概率,转移就将两个子树合并起来即可,设 (u)(v) 的父亲,那么显然 (dp_{u,0}) 只能转移到新的 (dp_{u,0}),而对于 (i e 0)(jin[0,siz_y])(dp_{u,i})(dp_{v,j})(dfrac{1}{2^{2ij}}) 的概率转移到 (dp_{u,i+j}),有 (1-dfrac{1}{2^{2ij}}) 的概率转移到 (dp_{u,0})。树上背包求一下即可。最终 (dp_{i,0}) 即为 (i) 作为某条路径 LCA 出现的概率。时间复杂度 (mathcal O(n^2))

    总结:为什么这样的题目要找出 (f(S)) 的等价表达?因为这里的 (f(S)) 是一个找最大值的形式,而求一车东西的 (max) 这个东西是很难计算的(当然有些情况下确实是可以,不过要用 Min-Max 容斥等技巧,并且局限性比较大),(当然有的数据范围很小的题目也可以用这个做法,比方说一些 DP 套 DP,如 TJOI2018 游园)。因此我们需要想办法将其转化为求和的形式,这时候就要找到它的等价表达。类似的题目还有 CF1067E Random Forest Rank。

    const int MAXN=5000;
    const int INV2=MOD+1>>1;
    int qpow(int x,int e){
    	int ret=1;
    	for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
    	return ret;
    }
    int n,ipw2[MAXN*MAXN+5],dp[MAXN+5][MAXN+5],res=0,siz[MAXN+5];
    link_list<int,MAXN,MAXN*2> g;
    void dfs(int x,int f){
    	dp[x][0]=dp[x][1]=INV2;siz[x]=1;
    	for(int e=g.hd[x];e;e=g.nxt[e]){
    		int y=g.val[e];if(y==f) continue;dfs(y,x);
    		static int tmp[MAXN+5];memset(tmp,0,sizeof(tmp));
    		for(int i=1;i<=siz[x];i++) for(int j=0;j<=siz[y];j++){
    			tmp[i+j]=(tmp[i+j]+1ll*dp[x][i]*dp[y][j]%MOD*ipw2[2*i*j])%MOD;
    			tmp[0]=(tmp[0]+1ll*dp[x][i]*dp[y][j]%MOD*(1-ipw2[2*i*j]+MOD))%MOD;
    		} siz[x]+=siz[y];for(int i=1;i<=siz[x];i++) dp[x][i]=tmp[i];
    		dp[x][0]=(dp[x][0]+tmp[0])%MOD;
    	} res=(res+dp[x][0])%MOD;//printf("%d %d
    ",x,dp[x][0]);
    }
    int main(){
    	freopen("thousands.in","r",stdin);
    	freopen("thousands.out","w",stdout);
    	scanf("%d",&n);
    	for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),g.ins(u,v),g.ins(v,u);
    	for(int i=(ipw2[0]=1);i<=n*n;i++) ipw2[i]=1ll*ipw2[i-1]*INV2%MOD;
    	dfs(1,0);printf("%d
    ",1ll*res*qpow(2,n*n)%MOD);
    	return 0;
    }
    
  • 相关阅读:
    linux之awk命令
    HDU 2097 Sky数 进制转换
    HDU 2077 汉诺塔IV
    HDU 2094 产生冠军 dfs加map容器
    HDU 2073 叠框
    HDU 2083 简易版之最短距离
    HDU 2063 过山车 二分匹配
    天梯 1014 装箱问题
    天梯 1214 线段覆盖
    天梯 1098 均分纸牌
  • 原文地址:https://www.cnblogs.com/ET2006/p/NFLSOJ-10317.html
Copyright © 2011-2022 走看看