zoukankan      html  css  js  c++  java
  • 【洛谷4757】[CERC2014] Parades(DP)

    点此看题面

    • 一棵(n)个点的树,满足每个点的度数小于等于(10)
    • 给定(m)条树上路径,求最多从中选出多少条路径,满足任意两条路径无重边。
    • (nle10^3,mle frac {n(n-1)}2)

    初步(DP)思路

    显然每棵子树最多向外连出一条路径,而能向外连出一条路径只需满足对应端点到该子树根路径上的边都未被选择。

    因此,容易想到设(f_{x,i})表示满足(i)(x)路径上的边都未被选择时,最多能在(x)的子树内选出多少条路径。

    状压(DP)合并子节点

    这题的一个良心性质就是每个点度数小于等于(10)

    考虑要合并(x)的子节点信息时,我们先根据(LCA)(x)的所有路径,预处理出(A_i)表示第(i)个子节点单独能产生的最大贡献,(B_{i,j})表示第(i)个和第(j)个子节点配对能产生的最大贡献。

    然后我们设(g_S)表示集合(S)中的子节点能产生的最大贡献,转移只需考虑新加入单独一个点还是一对点。

    预处理出(g)之后再要求(f_{x,i})就很简单了,假设(i)(x)的子节点(u)的子树中,记(S')为全集除去(u)之后的集合,则(f_{x,i}=g_{S'}+f_{u,i})

    代码:(O(n2^kk^2))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Rg register
    #define RI Rg int
    #define Cn const
    #define CI Cn int&
    #define I inline
    #define W while
    #define N 1000
    #define K 10 
    #define LN 10
    #define Gmax(x,y) (x<(y)&&(x=(y)))
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    using namespace std;
    int n,m,ee,lnk[N+5];struct edge {int to,nxt;}e[N<<1];vector<pair<int,int> > V[N+5];
    int D[N+5],fa[N+5][LN+1];I void dfs(CI x)//预处理倍增数组
    {
    	RI i;for(i=1;i<=LN;++i) fa[x][i]=fa[fa[x][i-1]][i-1];
    	for(i=lnk[x];i;i=e[i].nxt) e[i].to^fa[x][0]&&(D[e[i].to]=D[fa[e[i].to][0]=x]+1,dfs(e[i].to),0);
    }
    I int Jump(RI x,CI d)//求出x深度为d的祖先
    {
    	for(RI i=0;D[x]^d;++i) (D[x]^d)>>i&1&&(x=fa[x][i]);return x;
    }
    I int LCA(RI x,RI y)
    {
    	if(D[x]<D[y]&&(swap(x,y),0),(x=Jump(x,D[y]))==y) return x;
    	for(RI i=LN;~i;--i) fa[x][i]^fa[y][i]&&(x=fa[x][i],y=fa[y][i]);return fa[x][0];
    }
    int f[N+5][N+5],q[K+5],rk[N+5],g[1<<K],A[K+5],B[K+5][K+5];I void DP(CI x)//动态规划
    {
    	RI i,j;for(i=lnk[x];i;i=e[i].nxt) e[i].to^fa[x][0]&&(DP(e[i].to),0);//先DP完所有子节点
    	RI t=0;for(i=lnk[x];i;i=e[i].nxt) e[i].to^fa[x][0]&&(q[rk[e[i].to]=t++]=e[i].to);//记录子节点
    	for(i=0;i^t;++i) for(A[i]=f[q[i]][q[i]],j=0;j^t;++j) B[i][j]=0;//初始化A,B
    	RI u,v;for(vector<pair<int,int> >::iterator it=V[x].begin();it!=V[x].end();++it)//枚举所有以x为LCA的路径
    	{
    		if(it->second==x&&(swap(it->first,it->second),0),it->first==x)//一端是x
    			{u=Jump(it->second,D[x]+1),Gmax(A[rk[u]],f[u][it->second]+1);continue;}//更新A
    		u=Jump(it->first,D[x]+1),v=Jump(it->second,D[x]+1),rk[u]>rk[v]&&(swap(u,v),swap(it->first,it->second),0);
    		Gmax(B[rk[u]][rk[v]],f[u][it->first]+f[v][it->second]+1);//更新B
    	}
    	for(i=0;i^(1<<t);++i) g[i]=0;for(i=0;i^(1<<t);++i)//状压DP求g
    	{
    		for(u=0;u^t;++u) !(i>>u&1)&&Gmax(g[i|(1<<u)],g[i]+A[u]);//加入单独一个点
    		for(u=0;u^t;++u) if(!(i>>u&1)) for(v=u+1;v^t;++v) !(i>>v&1)&&Gmax(g[i|(1<<u)|(1<<v)],g[i]+B[u][v]);//加入一对点
    	}
    	for(i=1;i<=n;++i) f[x][i]=x^i?(D[x]<D[i]&&fa[u=Jump(i,D[x]+1)][0]==x?g[((1<<t)-1)^(1<<rk[u])]+f[u][i]:0):g[(1<<t)-1];//利用g求f
    }
    int main()
    {
    	RI Tt,i,x,y;scanf("%d",&Tt);W(Tt--)
    	{
    		for(scanf("%d",&n),ee=0,i=1;i<=n;++i) lnk[i]=0,V[i].clear();//注意清空
    		for(i=1;i^n;++i) scanf("%d%d",&x,&y),add(x,y),add(y,x);
    		for(dfs(1),scanf("%d",&m),i=1;i<=m;++i) scanf("%d%d",&x,&y),V[LCA(x,y)].push_back(make_pair(x,y));//把每条路径都存到LCA上
    		DP(1),printf("%d
    ",f[1][1]);
    	}return 0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    SharePoint 2013 安装.NET Framework 3.5 报错
    SharePoint 2016 配置工作流环境
    SharePoint 2016 站点注册工作流服务报错
    Work Management Service application in SharePoint 2016
    SharePoint 2016 安装 Cumulative Update for Service Bus 1.0 (KB2799752)报错
    SharePoint 2016 工作流报错“没有适用于此应用程序的地址”
    SharePoint 2016 工作流报错“未安装应用程序管理共享服务代理”
    SharePoint JavaScript API in application pages
    SharePoint 2016 每天预热脚本介绍
    SharePoint 无法删除搜索服务应用程序
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4757.html
Copyright © 2011-2022 走看看