zoukankan      html  css  js  c++  java
  • BZOJ 4042 Luogu P4757 [CERC2014]Parades (树形DP、状压DP)

    题目链接

    (BZOJ) https://www.lydsy.com/JudgeOnline/problem.php?id=4042
    (Luogu) https://www.luogu.org/problem/P4757

    题解

    挺神仙的题。
    观察到两个重要性质:
    (1) 只有不影响任何已选方案的时候,才需要去考虑是否要选择(u)的子树内往上走的链。(因为链不带权值)
    (2) 如果要选择(u)子树内往上走的链,那么最多选择一条。

    由此可知,我们可以记录哪些链在(u)子树内的所有方案中是必选的,所有非必选的都可视作空闲。因为往上走的链最多选择一条,所以如果这条链和一条非必选的边冲突,有办法调整最优方案使得这条链选上,而不会受到“两条非必选的边至少选一条”这种情况的影响。

    于是我们记录(dp[u])表示(u)子树内最多选择多少链以及(S[u])表示(u)子树内最优解非必选的端点,转移的时候先把所有的儿子拿出来,建一个图,两点(i,j)连边当且仅当存在(xin S[i],yin S[j], x,y)是输入的一条路径,然后用状压DP求它的最大匹配即可。设(dp0[i])表示(i)集合内的最大匹配,(U)为所有儿子的全集,那么若(dp0[U]=dp0[U-{i}])就说明(i)非必选,那么把(S[i])加入到(S[u])中。
    注意特殊处理以(u)为一条链的端点的情况。

    时间复杂度(O(n^2+nd2^d+m)).

    代码

    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<cassert>
    #include<vector>
    using namespace std;
    
    const int N = 1000;
    const int M = 5e5;
    const int D = 10;
    int lg2[(1<<D)+3];
    struct Edge
    {
    	int v,nxt;
    } e[(N<<1)+3];
    int fe[N+3];
    int fa[N+3];
    bool a[N+3][N+3];
    vector<int> ac[N+3];
    int dp[N+3];
    vector<int> son;
    bool ae[D+3][D+3];
    bool ae0[D+3];
    int dp0[(1<<D)+3];
    int n,en,m;
    
    void addedge(int u,int v)
    {
    	en++; e[en].v = v;
    	e[en].nxt = fe[u]; fe[u] = en;
    }
    
    void dfs(int u)
    {
    	dp[u] = 0;
    	for(int i=fe[u]; i; i=e[i].nxt)
    	{
    		int v = e[i].v;
    		if(v==fa[u]) continue;
    		fa[v] = u;
    		dfs(v);
    		dp[u] += dp[v];
    	}
    	son.clear();
    	for(int i=fe[u]; i; i=e[i].nxt)
    	{
    		int v = e[i].v;
    		if(v==fa[u]) continue;
    		son.push_back(v);
    	}
    	for(int i=0; i<son.size(); i++)
    	{
    		for(int j=i+1; j<son.size(); j++)
    		{
    			ae[i][j] = ae[j][i] = false;
    			for(int ii=0; ii<ac[son[i]].size(); ii++)
    			{
    				for(int jj=0; jj<ac[son[j]].size(); jj++)
    				{
    					if(a[ac[son[i]][ii]][ac[son[j]][jj]])
    					{
    						ae[i][j] = ae[j][i] = true; break;
    					}
    				}
    			}
    		}
    		ae0[i] = false;
    		for(int ii=0; ii<ac[son[i]].size(); ii++) {if(a[ac[son[i]][ii]][u]) {ae0[i] = true; break;}}
    	}
    	dp0[0] = 0;
    	for(int i=1; i<(1<<son.size()); i++)
    	{
    		int x = lg2[i&(-i)];
    		dp0[i] = dp0[i^(1<<x)];
    		if(ae0[x]) {dp0[i]++;}
    		for(int j=0; j<son.size(); j++)
    		{
    			if(i&(1<<j))
    			{
    				if(ae[x][j]) {dp0[i] = max(dp0[i],dp0[i^(1<<x)^(1<<j)]+1);}
    			}
    		}
    	}
    	dp[u] += dp0[(1<<son.size())-1];
    	for(int i=0; i<son.size(); i++)
    	{
    		if(dp0[(1<<son.size())-1]==dp0[((1<<son.size())-1)^(1<<i)])
    		{
    			for(int j=0; j<ac[son[i]].size(); j++) {ac[u].push_back(ac[son[i]][j]);}
    		}
    	}
    	ac[u].push_back(u);
    }
    
    int main()
    {
    	for(int i=0; i<=D; i++) lg2[1<<i] = i;
    	int T; scanf("%d",&T);
    	while(T--)
    	{
    		scanf("%d",&n);
    		for(int i=1; i<n; i++)
    		{
    			int u,v; scanf("%d%d",&u,&v);
    			addedge(u,v); addedge(v,u);
    		}
    		scanf("%d",&m);
    		for(int i=1; i<=m; i++)
    		{
    			int u,v; scanf("%d%d",&u,&v);
    			a[u][v] = a[v][u] = 1;
    		}
    		dfs(1);
    		printf("%d
    ",dp[1]);
    		for(int i=1; i<=n; i++) fe[i] = fa[i] = 0,ac[i].clear();
    		for(int i=1; i<=en; i++) e[i].v = e[i].nxt = 0;
    		for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) a[i][j] = a[j][i] = 0;
    		n = en = m = 0;
    	}
    	return 0;
    }
    
  • 相关阅读:
    双向循环链表
    字符串拷贝
    div样式调整.txt
    解析xml的单个节点值和循环节点消息体
    C++中的string
    正则表达式教程
    一个很好的Qt教程个人主页
    单射、双射与满射
    ISO C++ forbids declaration of * with no type
    一个中学生的个人主页
  • 原文地址:https://www.cnblogs.com/suncongbo/p/11491120.html
Copyright © 2011-2022 走看看