P3621 [APIO2007]风铃

Posted thedreammaker

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3621 [APIO2007]风铃相关的知识,希望对你有一定的参考价值。

这个故事告诉我们,万物皆可暴(du)力(liu)dp。
首先,题目的大意就是让我们通过他给定的一些变换方式使得这棵树变为完全二叉树。我们把完全二叉树所有的叶子节点连起来,应该只有两种情况,一种是一条链,另一种是两条链,其中一条链上的点的深度比另一条链的深度大1。
令dp(i,j)表示i节点状态为j时的最小移动次数。
状态无非三种:
1.一条较深的链。
2.一条较浅的链。
3.两条链。
我们注意到,对于一个点,不管怎么移动,深度总是不变,所以对于状态1和状态2,不需要向下递归,只需要查看这棵子树内所有的叶子节点的深度是否为对应值就可以了。

第三种情况的转移就是下面六种情况:
1.1+2
2.1+3
3.3+2
4.2+1(+1)
5.3+1(+1)
6.2+3(+1)加一指的是左右子树交换的花费

代码在下面:

#pragma GCC optimize("Ofast,fast-math")
#pragma GCC target("avx,avx2")
#pragma GCC optimize("O2")
#include<bits/stdc++.h>
#define gc pa==pb&&(pb=(pa=buf)+fread(buf,1,1<<17,stdin),pa==pb)?EOF:*pa++
static char buf[1<<17],*pa(buf),*pb(buf);
inline int read() 
	register int s=0,f=1;
	register char ch(gc);
	while(ch<‘0‘||ch>‘9‘) ch==‘-‘?f=-1,ch=gc:ch=gc;
	while(ch>=‘0‘&&ch<=‘9‘) s=s*10+ch-48,ch=gc;
	return s*f;

using namespace std;
struct yuansu 
	int x,y;
 bian[200005];
int minn(10000000);
int b[100005],dui[100005],deepth[100005];
int lef[100005],rig[100005];
int f(1);
int n,x,y;
int possible[100005][2];
int cmp(yuansu a,yuansu b) 
	return a.x<b.x;

int min(int a,int b) 
	return a<b?a:b;

int bfs() 
	int head(1),tail(2);
	b[0]=b[1]=1;
	dui[1]=1;
	while(head!=tail) 
		int i(dui[head]);
		if(!lef[i]) 
			if(minn!=10000000) 
				if((minn>deepth[dui[head]]+1)||(minn<deepth[dui[head]]-1))return 1;
			
			if(minn!=deepth[dui[head]]&&minn!=10000000)f=0;
			minn=min(minn,deepth[dui[head]]);
		
		if(!rig[i]) 
			if(minn!=10000000) 
				if((minn>deepth[dui[head]]+1)||(minn<deepth[dui[head]]-1))return 1;
			
			if(minn!=deepth[dui[head]]&&minn!=10000000)f=0;
			minn=min(minn,deepth[dui[head]]);
		
		if(!b[lef[i]]) 
			dui[tail]=lef[i];
			deepth[dui[tail]]=deepth[dui[head]]+1;
			tail++;
		
		if(!b[rig[i]]) 
			dui[tail]=rig[i];
			deepth[dui[tail]]=deepth[dui[head]]+1;
			tail++;
		
		head++;
	
	return 0;

void dfs(int dian) 
	if(!lef[dian]) 
		if(deepth[dian]==minn)
			possible[dian][0]=1;//若该点为浅点,则包含该点的子树不可能出现深状态(也就是0状态)
		else
			possible[dian][1]=1;
	
	if(!rig[dian]) 
		if(deepth[dian]==minn)
			possible[dian][0]=1;
		else
			possible[dian][1]=1;
	
	if(lef[dian]) 
		dfs(lef[dian]);
		possible[dian][0]|=possible[lef[dian]][0];
		possible[dian][1]|=possible[lef[dian]][1];
	
	if(rig[dian]) 
		dfs(rig[dian]);
		possible[dian][0]|=possible[rig[dian]][0];
		possible[dian][1]|=possible[rig[dian]][1];
	
	return;

void chushihua() 
	dfs(1);
	return;

int violence_dp(int dian,int zt) 
	if(zt==0||zt==1) 
		if(!possible[dian][zt])return 0;
		else return 100000000;
	 else 
		int ans(100000000);
		if(lef[dian]==0&&rig[dian]==0)return 100000000;
		else if(!lef[dian]&&rig[dian]) 
			if(deepth[dian]==minn) 
				ans=min(ans,violence_dp(rig[dian],0)+1);//1+0 (+1)
				ans=min(ans,violence_dp(rig[dian],2)+1);//1+2 (+1)
			 else 
				ans=min(ans,violence_dp(rig[dian],1));//0+1
				ans=min(ans,violence_dp(rig[dian],2));//0+2
			
		 else if(lef[dian]&&!rig[dian]) 
			if(deepth[dian]==minn) 
				ans=min(ans,violence_dp(lef[dian],0));//0+1
				ans=min(ans,violence_dp(lef[dian],2));//2+1
			 else 
				ans=min(ans,violence_dp(lef[dian],1)+1);//1+0 (+1)
				ans=min(ans,violence_dp(lef[dian],2)+1);//2+0 (+1)
			
		 else 
			ans=min(ans,violence_dp(lef[dian],0)+violence_dp(rig[dian],1));//0+1
			ans=min(ans,violence_dp(lef[dian],2)+violence_dp(rig[dian],1));//2+1
			ans=min(ans,violence_dp(lef[dian],0)+violence_dp(rig[dian],2));//0+2
			ans=min(ans,violence_dp(lef[dian],1)+violence_dp(rig[dian],0)+1);//1+0 (+1)
			ans=min(ans,violence_dp(lef[dian],1)+violence_dp(rig[dian],2)+1);//1+2 (+1)
			ans=min(ans,violence_dp(lef[dian],2)+violence_dp(rig[dian],0)+1);//2+0 (+1)
		
		return ans;
	

int main() 
	freopen("mobiles.in","r",stdin);
	freopen("mobiles.out","w",stdout);
	n=read();
	for(int i=1; i<=n; i++) 
		x=read();
		y=read();
		if(x>0) 
			lef[i]=x;
		
		if(y>0) 
			rig[i]=y;
		
	
	if(bfs()) 
		cout<<-1;
		return 0;
	
	chushihua();
	if(f) //判断是不是一颗满二叉树
		cout<<0;
		return 0;
	
	if(violence_dp(1,2)>10000000)cout<<-1;
	else cout<<violence_dp(1,2);
	return 0;

 


    
    chushihua();
    if(f) //判断是不是一颗满二叉树
        cout<<0;
        return 0;
    
    if(violence_dp(1,2)>10000000)cout<<-1;
    else cout<<violence_dp(1,2);
    return 0;


以上是关于P3621 [APIO2007]风铃的主要内容,如果未能解决你的问题,请参考以下文章

APIO2007 风铃

[APIO/ctsc2007]

[CTSC2007][APIO2007]数据备份Backup

[APIO2007]动物园 --- 状压DP

P3620 [APIO/CTSC 2007]数据备份

P3620 [APIO/CTSC 2007] 数据备份