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;
    }
    
  • 相关阅读:
    Oracle11g客户端安装记要202011+Navicat Premium 连接Oracle 数据库
    设计模式之 ==> 观察者计模式
    设计模式之 ==> 责任链计模式
    一个.NET Core下的开源插件框架
    JavaScript判断文件是否为UTF-8编码
    Postgresql 设置字段搜索支持中文排序
    springboot docker 显示中文乱码问题
    linux 更新系统时间
    怎么让网易云音乐等音乐软件的歌词在MacBook Pro的 touch bar(触摸栏)上显示
    数仓:解读 NameNode 的 edits 和 fsimage 文件内容
  • 原文地址:https://www.cnblogs.com/ET2006/p/NFLSOJ-10317.html
Copyright © 2011-2022 走看看