1 条题解
-
0
自动搬运
来自洛谷,原作者为

ez_lcw
**搬运于
2025-08-24 22:03:18,当前版本为作者最后更新于2019-09-15 07:27:11,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
考虑树形,设表示以为根的子树中路线的最大数量(即这棵子树的最优解),表示以为根的子树中,所有的点,当且仅当到的路径上不与子树的最优解中的某条路径相交。
那么考虑用子树合并根。
对于,我们先加上每个儿子的值,然后我们再考虑下面这两种情况:
-
对于的某一个儿子,如果的子树内(包括自己)有一个点与之间有路径,如图:
可能对于某一个儿子,这种路径可能有很多条,但是选任意一条都可以,因为边只能走一遍。 -
对于的某两个不同的儿子、,它们各自子树内(包括、)分别有两个点、,且有一条路径,我们就把设为,如图:
那么对于某个儿子,它可能可以匹配到很多个,所以怎么选择才方案最优是解决问题的关键,所以我们考虑状压。设表示状态的最优解,状态的二进制表达式的第位若为,则表示已经考虑过加入这个儿子的情况了。
那么我们从小到大枚举,找到,,则可以从转移过来,因为在的二进制表达式中,第位为;在的二进制表达式中,第位为。
所以现在我们考虑的是考虑过加入儿子,也就是上文的,那么我们还要枚举,即下文的。
对于每一个,当且的二进制表达式中第位为时,我们取:
即从的第位为且第位为时转移过来。
最后即可。
这样就合并完成了。
记得合并完后维护一下。
最后输出就好了。
完整代码加注释如下:
#include<bits/stdc++.h> #define N 1010 #define D 15 using namespace std; int T,n,m; int dp[N],f[1<<D]; int cnt,head[N],nxt[N<<1],to[N<<1]; bool vis[N][N],link[D][D]; vector<int>un[N]; int lowbit(int x) { return x&-x; } void init() { cnt=0; memset(head,0,sizeof(head)); memset(vis,false,sizeof(vis)); memset(dp,0,sizeof(dp)); } void adde(int u,int v) { to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; } void dfs(int u,int fa) { int son[D],tot=0; for(int i=head[u];i;i=nxt[i]) { if(to[i]!=fa) { dfs(to[i],u); dp[u]+=dp[to[i]]; son[tot++]=to[i]; //存储每一个儿子 } } for(int i=0;i<tot;i++) { for(int j=0;j<un[son[i]].size();j++) { if(vis[un[son[i]][j]][u]) { dp[u]++; un[son[i]].clear();//由于已经选了一条边了,所以为了下面的维护un[u],我们把un[son[i]]清零 } } } memset(link,false,sizeof(link)); for(int i=0;i<tot;i++) { for(int j=i+1;j<tot;j++) { int a=son[i],b=son[j]; for(int p=0;p<un[a].size();p++) { for(int q=0;q<un[b].size();q++) { if(vis[un[a][p]][un[b][q]]) { link[i][j]=link[j][i]=true;//标记son[i]与son[j]各自子树之间有路径 break; } } if(link[i][j]) break; } } } f[0]=0; int maxn=(1<<tot)-1; for(int i=1;i<=maxn;i++) { f[i]=f[i-lowbit(i)]; int a=__builtin_ctz(i); for(int b=a+1;b<tot;b++)//枚举son2 if(link[a][b]&&((i>>b)&1)) f[i]=max(f[i],f[i-(1<<a)-(1<<b)]+1); } dp[u]+=f[maxn]; un[u].push_back(u); for(int i=0;i<tot;i++) if(f[maxn]==f[maxn-(1<<i)]) for(int j=0;j<un[son[i]].size();j++) un[u].push_back(un[son[i]][j]);//维护un[u] } int main() { scanf("%d",&T); while(T--) { init(); scanf("%d",&n); for(int i=1;i<=n;i++) un[i].clear(); for(int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); adde(u,v),adde(v,u); } scanf("%d",&m); for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); vis[u][v]=vis[v][u]=true; } dfs(1,0); printf("%d\n",dp[1]); } } -
- 1
信息
- ID
- 3747
- 时间
- 1500ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者