51nod 1673 树有几多愁——虚树+状压DP
题目:http://www.51nod.com/Challenge/Problem.html#!#problemId=1673
建一个虚树。
一种贪心的想法是把较小的值填到叶子上,这样一个小值限制到的叶子比较少。
但不太会贪心了,所以考虑 DP 。只有 20 个叶子,(不是用来暴搜的!)可以状压DP了。
dp[ S ]表示选了点集 S 的叶子的方案数。再记一个 ct[ S ] 表示选这个点集的叶子、不影响到其他叶子,最多可以填几个点。
dp[ S ]可以枚举最后一个填的是哪个叶子来转移;ct[ S ]可以在第一次枚举的时候就算出来,就是看这个 “最后一个填上的叶子” 多带来几个可以填的点。
不过这样就不知道复杂度是怎样的了。反正还是过了。
- #include<cstdio>
- #include<cstring>
- #include<algorithm>
- #define ll long long
- #define db double
- using namespace std;
- int rdn()
- {
- int ret=;bool fx=;char ch=getchar();
- while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
- while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
- return fx?ret:-ret;
- }
- ll Mx(ll a,ll b){return a>b?a:b;}
- ll Mn(ll a,ll b){return a<b?a:b;}
- const int N=1e5+,K=,M=(<<)+,mod=1e9+;
- int n,hd[N],xnt,to[N<<],nxt[N<<],dfn[N],tim;
- int bin[K],lg[M],dep[N],pre[N][K],siz[N]; bool vis[N];
- int get_lca(int x,int y)
- {
- if(dep[x]<dep[y])swap(x,y);
- int d=dep[x]-dep[y];
- for(int t=;bin[t]<=d;t++)
- if(d&bin[t])x=pre[x][t];
- if(x==y)return x;
- for(int t=;t>=;t--)
- if(pre[x][t]!=pre[y][t])
- x=pre[x][t], y=pre[y][t];
- return pre[x][];
- }
- namespace Tr{
- const int tN=;
- int hd[N],xnt,to[tN],nxt[tN],fa[N];
- int sta[tN],top,p[tN],tot;
- int bh[N],dy[tN],vl[N],ct[M];
- int dp[M]; db d2[M];
- bool cmp(int u,int v){return dfn[u]<dfn[v];}
- void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;fa[y]=x;}
- void ini_dfs(int cr)
- {
- if(vis[cr])vl[cr]=bin[bh[cr]-];
- for(int i=hd[cr],v;i;i=nxt[i])
- {
- ini_dfs(v=to[i]);
- vl[cr]|=vl[v];
- }
- }
- void init()
- {
- sort(p+,p+tot+,cmp);
- sta[top=]=p[];
- for(int i=;i<=tot;i++)
- {
- int u=p[i], lca=get_lca(u,sta[top]);
- while(top&&dfn[lca]<dfn[sta[top]])
- {
- if(dfn[sta[top-]]<dfn[lca])
- add(lca,sta[top]);
- else add(sta[top-],sta[top]);
- top--;
- }
- if(sta[top]!=lca)sta[++top]=lca;
- sta[++top]=u;
- }
- for(int i=;i<top;i++)add(sta[i],sta[i+]);
- ini_dfs(sta[]);
- }
- void solve()
- {
- init();
- for(int i=,S;i<=tot;i++)
- {
- S=bin[i-]; dp[S]=; d2[S]=;
- int cr=dy[i];//dy not bh
- while(vl[fa[cr]]==S)cr=fa[cr];
- ct[S]=siz[cr]+(dep[cr]-dep[fa[cr]]-);
- }
- for(int S=;S<bin[tot];S++)
- {
- if(lg[S])continue;
- int T=(S&-S), cr=dy[lg[T]+], pr=cr; T=S^T;
- dp[S]=(ll)dp[T]*(ct[T]+)%mod;
- d2[S]=d2[T]*(ct[T]+);
- while(cr&&(vl[cr]|S)==S)//token!!
- cr=fa[cr];
- // if(!cr) then ct[S] is wrong but has no influence
- ct[S]=ct[T]+dep[pr]-dep[cr];
- int tp=T;
- while(tp)
- {
- T=(tp&-tp); tp^=T; T=S^T;
- db tmp=d2[T]*(ct[T]+);
- if(tmp>d2[S])
- dp[S]=(ll)dp[T]*(ct[T]+)%mod,
- d2[S]=d2[T]*(ct[T]+);
- }
- }
- printf("%d\n",dp[bin[tot]-]);
- }
- }
- void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
- void dfs(int cr,int fa)
- {
- dfn[cr]=++tim;
- siz[cr]=; dep[cr]=dep[fa]+;
- pre[cr][]=fa;
- for(int t=;bin[t]<=dep[cr];t++)
- pre[cr][t]=pre[pre[cr][t-]][t-];
- bool flag=;
- for(int i=hd[cr],v;i;i=nxt[i])
- if((v=to[i])!=fa)
- {
- flag=;
- dfs(v,cr);siz[cr]+=siz[v];
- }
- if(!flag)
- {
- vis[cr]=; Tr::p[++Tr::tot]=cr;
- Tr::bh[cr]=Tr::tot;
- Tr::dy[Tr::tot]=cr;
- }
- }
- int main()
- {
- n=rdn();
- for(int i=,u,v;i<n;i++)
- u=rdn(),v=rdn(),add(u,v),add(v,u);
- bin[]=;
- for(int i=;i<=;i++)
- bin[i]=bin[i-]<<,lg[bin[i]]=i;
- dfs(,); Tr::solve();
- return ;
- }
51nod 1673 树有几多愁——虚树+状压DP的更多相关文章
- 刷题总结——树有几多愁(51nod1673 虚树+状压dp+贪心)
题目: lyk有一棵树,它想给这棵树重标号. 重标号后,这棵树的所有叶子节点的值为它到根的路径上的编号最小的点的编号. 这棵树的烦恼值为所有叶子节点的值的乘积. lyk想让这棵树的烦恼值最大,你只需输 ...
- [51nod1673]树有几多愁
lyk有一棵树,它想给这棵树重标号. 重标号后,这棵树的所有叶子节点的值为它到根的路径上的编号最小的点的编号. 这棵树的烦恼值为所有叶子节点的值的乘积. lyk想让这棵树的烦恼值最大,你只需输出最大烦 ...
- 【62测试】【状压dp】【dfs序】【线段树】
第一题: 给出一个长度不超过100只包含'B'和'R'的字符串,将其无限重复下去. 比如,BBRB则会形成 BBRBBBRBBBRB 现在给出一个区间[l,r]询问该区间内有多少个字符'B'(区间下标 ...
- bzoj 4006 [JLOI2015]管道连接(斯坦纳树+状压DP)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=4006 [题意] 给定n点m边的图,连接边(u,v)需要花费w,问满足使k个点中同颜色的 ...
- bzoj1402 Ticket to Ride 斯坦纳树 + 状压dp
给定\(n\)个点,\(m\)条边的带权无向图 选出一些边,使得\(4\)对点之间可达,询问权值最小为多少 \(n \leqslant 30, m \leqslant 1000\) 首先看数据范围,\ ...
- 【bzoj4006】[JLOI2015]管道连接 斯坦纳树+状压dp
题目描述 给出一张 $n$ 个点 $m$ 条边的无向图和 $p$ 个特殊点,每个特殊点有一个颜色.要求选出若干条边,使得颜色相同的特殊点在同一个连通块内.输出最小边权和. 输入 第一行包含三个整数 n ...
- BZOJ2595 Wc2008 游览计划 【斯坦纳树】【状压DP】*
BZOJ2595 Wc2008 游览计划 Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个 ...
- luogu4294 [WC2008]游览计划(状压DP/斯坦纳树)
link 题目大意:给定一个网格图,有些点是关键点,选择格点有代价,求把所有关键点联通的最小代价 斯坦纳树模板题 斯坦纳树问题:给定一个图结构,有一些点是关键点,求把这些关键点联通的最小代价e 斯坦纳 ...
- [bzoj4006][JLOI2015]管道连接_斯坦纳树_状压dp
管道连接 bzoj-4006 JLOI-2015 题目大意:给定一张$n$个节点$m$条边的带边权无向图.并且给定$p$个重要节点,每个重要节点都有一个颜色.求一个边权和最小的边集使得颜色相同的重要节 ...
随机推荐
- iOS UI-UIScrollView控件实现图片轮播 (UIPageControl-分页指示器)
一.实现效果 实现图片的自动轮播 二.实现代码 storyboard中布局 代码: #import "ViewController.h" #define HM ...
- Android 注解的使用与注意事项
一般情况下我们最常用到的三个注解分别是@EActivity @ViewById 和@Click @EActivity这个注解是用来修饰Activity的,向Activity注入布局,功能相当 ...
- PAIR PROJECTS 附加题
[附加题] 改进电梯调度的interface 设计, 让它更好地反映现实, 更能让学生练习算法, 更好地实现信息隐藏和信息共享. 答:首先,就原有的StopAtEach方法而言,此算法的实现既是非常低 ...
- addslash()
php addslashes函数的作用是在预定义的字符前面加上反斜杠,这些预定义字符包括: 单引号(') 双引号(") 反斜杠(\) NULL addslashes函数经常使用在向数据库插入 ...
- BadUSB测试记录
0x00 前言 不是很新的东西,其他作者已对此做过研究测试,本文仅用来记录操作过程,保存日志,说明细节. 0x01参考资料 https://github.com/adamcaudill/Psychso ...
- UINavigationController详细(转)
UINavigationController使用详解 有一阵子没有写随笔,感觉有点儿手生.一个多月以后终于又一次坐下来静下心写随笔,记录自己的学习笔记,也希望能够帮到大家. 废话少说回到正题,UINa ...
- SWIFT 之CoreData初试
SWIFT中使用CoreData来保存本地数据,在建立项目的时候把 "Use Core Data"选项选上 项目建立完成后点击后缀为 .xcdatamodeld的那个文件,点击右下 ...
- Ubuntu中敲写c语言步骤
1.进入Ubuntu界面 1.1按快捷键 Ctrl + Alt + T: 1.2 安装vim:输入 sudo apt-get install vim: 1.3 安装gcc:输入 sudo apt-ge ...
- HDU - 2475:Box(splay维护森林)
There are N boxes on the ground, which are labeled by numbers from 1 to N. The boxes are magical, th ...
- fullPage的使用
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>& ...