树论_1.

树形dp来一个Maximum Weight Subset大部分其实都是cf的题了。
看看看。考虑从dp入手后大部分的树形dp都要记录两维,那么再看看不妨记 f[ x ][ i ]表示以x为根的子树中最近的那个选择了的点深度为j。
那么明显可以从子节点转移,如何?通过枚举深度即可。

#include<bits/stdc++.h>
using namespace std;
int n,m,k,len=0,last[100001],f[1001][1001],a[10001],tmp[1001];
struct pp
{
	int x,y,next;
};pp p[100001];
void ins(int x,int y)
{
	int now=++len;
	p[now]={x,y,last[x]};last[x]=now;
	return ;
}
void dfs(int x,int fa)
{
	f[x][0]=a[x];
	for(int i=last[x];i!=-1;i=p[i].next)
	{
		int y=p[i].y;if(y==fa) continue ;dfs(y,x);
		for(int j=0;j<=n;j++) tmp[j]=f[x][j];
		for(int j=0;j<=n;j++) 
		{
			for(int kk=max(k-j,0);kk<=n;kk++) tmp[min(j,kk+1)]=max(tmp[min(j,kk+1)],f[x][j]+f[y][kk]);
		}	
		for(int j=0;j<=n;j++) f[x][j]=tmp[j];
	}
	return ;
}
int main()
{
	memset(last,-1,sizeof(last));
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n-1;i++) 
	{
		int x,y;scanf("%d%d",&x,&y);
		ins(x,y);ins(y,x); 
	}
	dfs(1,1);int ans=0;
	for(int i=0;i<=n;i++) ans=max(ans,f[1][i]);
	printf("%d",ans);
	return 0;
}

1-Trees and Queries,对于一个询问,可能的结果会有三种:
a->b, a -> x -> y -> b, a -> y -> x -> b。
那么考虑如何处理明显可以通过深度+lca爆杀。

#include<bits/stdc++.h>
using namespace std;
int n,m,len=0,last[100001],db[100001][30],dep[100001];
struct pp
{
	int x,y,next;
};pp p[400001];
void ins(int x,int y)
{
	int now=++len;
	p[now]={x,y,last[x]};last[x]=now;
	return ;
} 
void dfs(int x,int fa)
{
	dep[x]=dep[fa]+1,db[x][0]=fa; 
	for(int i=1;(1<<i)<=dep[x];i++) db[x][i]=db[db[x][i-1]][i-1];
	for(int i=last[x];i!=-1;i=p[i].next)
	{
		int y=p[i].y;if(y==fa) continue ;
		dfs(y,x);
	}
	return ;
}
int getlca(int x,int y)
{
	if(dep[x]>dep[y]) swap(x,y);
	for(int i=20;i>=0;i--) 
	{
		if(dep[y]-(1<<i)>=dep[x]) y=db[y][i];
	}
	if(x==y) return x;
	for(int i=20;i>=0;i--)
	{
		if(db[x][i]!=db[y][i]) x=db[x][i],y=db[y][i];
	}
	return db[x][0];
} 
int getdep(int x,int y)
{
	return dep[x]+dep[y]-dep[getlca(x,y)]*2;
}
bool check(int x,int k)
{
	if(x>k||(x%2!=k%2)) return false;
	return true;
}
int main()
{
	memset(last,-1,sizeof(last));
	scanf("%d",&n);
	for(int i=1;i<=n-1;i++)
	{
		int x,y;scanf("%d%d",&x,&y);
		ins(x,y);ins(y,x);
	}dfs(1,1);
	scanf("%d",&m);while(m--)
	{
		int a,b,x,y,k;scanf("%d%d%d%d%d",&x,&y,&a,&b,&k);
		int ab=getdep(a,b),ax=getdep(a,x),ay=getdep(a,y),bx=getdep(b,x),by=getdep(b,y),xy=getdep(x,y);
		if(check(ab,k)||check(ax+by+1,k)||check(ay+bx+1,k)) printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

Tree Queries,转化一下题面,发现每一个点其实都与它的父亲有关(又名若是要合法则父亲是必须的),那么题目可以转化成能否有一条从1到不知道哪个点的路径包含一些点,那么可以直接做了吧~好吧提一下我没想出来的点,也就是如何判断一个点是否在另一个点的子树内,怎么办呢,sb你树剖没学过嘛?

#include<bits/stdc++.h>
using namespace std;
int n,m,len=0,last[4000001],dfs_x=0,dfsx[4000001],siz[4000001],fa[4000001];
int a[4000001],dep[4000001];
struct pp
{
	int x,y,next;
};pp p[4000001];
void ins(int x,int y)
{
	int now=++len;
	p[now]={x,y,last[x]};last[x]=now;
	return ;
}
void dfs(int x,int ff)
{
	dfsx[x]=++dfs_x;siz[x]=1;fa[x]=ff;dep[x]=dep[ff]+1;
	for(int i=last[x];i!=-1;i=p[i].next)
	{
		int y=p[i].y;if(ff==y) continue ;
		dfs(y,x);siz[x]+=siz[y];
	}
	return ;
}
bool cmp(const int &x,const int &y)
{
	return dep[x]<dep[y];
}
bool check(int x,int y)
{
	if(dfsx[x]<=dfsx[y]&&dfsx[x]+siz[x]>dfsx[y]) return true;
	return false; 
}
int main()
{
	memset(last,-1,sizeof(last));
	int q;scanf("%d%d",&n,&q);
	for(int i=1;i<=n-1;i++)
	{
		int x,y;scanf("%d%d",&x,&y);
		ins(x,y);ins(y,x);
	}dfs(1,1);
	while(q--)
	{
		int k;scanf("%d",&k);
		for(int i=1;i<=k;i++) scanf("%d",&a[i]),a[i]=fa[a[i]];
		sort(a+1,a+k+1,cmp);int pd=1;
		for(int i=1;i<=k-1;i++) 
		{
			if(!check(a[i],a[i+1])) pd=0;
		}
		if(pd) printf("YES\n");
		else printf("NO\n"); 
	}
	return 0;  
}

Maximum White Subtreedp?

#include<bits/stdc++.h>
using namespace std;
int n,m,len=0,last[1000001],f[1000001],a[1000001];
struct pp
{
	int x,y,next;
};pp p[1000001];
void ins(int x,int y)
{
	int now=++len;
	p[now]={x,y,last[x]};last[x]=now;
	return ;
}
void dfs1(int x,int fa)
{
	for(int i=last[x];i!=-1;i=p[i].next)
	{
		int y=p[i].y;if(y==fa) continue ;
		dfs1(y,x);f[x]+=max(0,f[y]); 
	} 
	if(a[x]==0) f[x]--;
	else f[x]++; 
	return ;
}
void dfs2(int x,int fa)
{
	if(f[x]>0) f[x]=max(f[x],f[fa]);
	else f[x]=max(f[x],f[x]+f[fa]);
	for(int i=last[x];i!=-1;i=p[i].next)
	{
		int y=p[i].y;if(y==fa) continue ;
		dfs2(y,x);
	}
	return ;
}
int main()
{
	memset(last,-1,sizeof(last));
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n-1;i++)
	{
		int x,y;scanf("%d%d",&x,&y);
		ins(x,y);ins(y,x);
	}
	dfs1(1,1);dfs2(1,1);
	for(int i=1;i<=n;i++) printf("%d ",f[i]);
	return 0;
}

Construct the Binary Tree有意思的构造题,不妨做做看?

//考虑构造,若是一颗二叉树,则距离最大应该是不断向下衍生的,所以链状的时候距离最长。
//那么最小的时候呢?便是满二叉树的时候,其实若是难以理解,你可以尝试从每一个点的贡献考虑
//说出了判断方法,从判断得到启发,那么考虑构造,想了两种:
//第一种首先我们可以假设整一个链,然后再向上移节点,不过实现很麻烦?
//第二种其实我们可以直接看大佬的。嗯不是我懒,主要是大佬nb 
//总结下伟大的思想,化简的方式很简单,并且发现其对二进制的应用:i&(i-1)来判断其是否到下层
//其次将最左的1 2 4 8作为链使用我是非常认可的。 
#include<bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,sum,cnt=0,sumdep;
int dep[200011],fa[200011];
signed main()
{
	int t;scanf("%lld",&t);
	while(t--)
	{
		int pos ;memset(fa,0,sizeof(fa));memset(dep,0,sizeof(dep)); 
		scanf("%lld%lld",&n,&sumdep);dep[0]=-1;cnt=0;
		for(int i=1;i<=n;i++)
		{
			fa[i]=i>>1;dep[i]=dep[i>>1]+1;cnt+=dep[i];
			if((i&(i-1))==0) pos=i;//如果是更下一层,pos记录的是最低端 
		}
		sumdep-=cnt;
		if(sumdep<0) 	
		{
			printf("NO\n");
			continue ;
		}
		if(sumdep==0)
		{
			printf("YES\n");
			for(int i=2;i<=n;i++) printf("%lld ",fa[i]);
			printf("\n");continue ;
		}
		for(int i=n;i;i--)
		{
			if((i&(i-1))==0) continue ;
			sumdep-=dep[pos]-dep[i]+1;
			if(sumdep<=0)
			{
				while(sumdep) pos=fa[pos],sumdep++;
				sumdep=0;fa[i]=pos;break;
			}
        	fa[i]=pos,dep[i]=dep[pos]+1,pos=i;
		}
		if(sumdep) printf("NO\n");
		else 
		{
			printf("YES\n");
			for(int i=2;i<=n;i++) printf("%lld ",fa[i]);
			printf("\n");	
		}
	}
	return 0;
} 

Village (Minimum)Village (Maximum)欸这两道题怎么说呢我们先做最大值看看。

//这题我们应该考虑从边的贡献入手,考虑每条边最多能被经过几次,则对于左右两个点而言
//那么应该是min(siz[x],siz[y])*2,那么考虑如何达到理论上的最大值,发现min的值最多为n/2
//也就是说找一个点使其子树的最大值不超过n/2,这不禁让人想到一个东西——重心!
//那么明显肯定要从重心入手,想维护。
//呃呃呃考虑了半天,看了大佬的题解,发现因为找到了重心,将子树按dfsx扔入队列中 
//那么发现i与i+n/2是绝不在同一颗子树中的,所以直接这样维护即可,再想想证明,发现每个点都做到了最优贡献,所以这样维护是行的
#include<bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,len=0,last[400001],maxx[400001],siz[400001],q[400001],tot=0;
int root,ans[400001],sum=0,dep[400001];
struct pp
{
	int x,y,next;
};pp p[400001];
void ins(int x,int y)
{
	int now=++len;
	p[now]={x,y,last[x]};last[x]=now;
	return ;
}
void dfs1(int x,int fa)
{
	siz[x]=1;
	for(int i=last[x];i!=-1;i=p[i].next)
	{
		int y=p[i].y;if(y==fa) continue ;
		dfs1(y,x);siz[x]+=siz[y];
		maxx[x]=max(maxx[x],siz[y]);
	}
	maxx[x]=max(maxx[x],n-siz[x]);
	if(!root||maxx[root]>maxx[x]) root=x;
	return ;
}
void dfs2(int x,int fa)
{
	dep[x]=dep[fa]+1;q[++tot]=x;
	for(int i=last[x];i!=-1;i=p[i].next)
	{
		int y=p[i].y;if(y==fa) continue ;
		dfs2(y,x);
	}
	return ;
}
 main()
{
	memset(last,-1,sizeof(last));
	scanf("%lld",&n);root=0;
	for(int i=1;i<=n-1;i++)
	{
		int x,y;scanf("%lld%lld",&x,&y);
		ins(x,y);ins(y,x); 
	}
	dfs1(1,1);dep[root]=-1;dfs2(root,root);
	for(int i=1;i<=n/2;i++)
	{
		int x=q[i],y=q[i+n/2];
		sum+=(dep[x]+dep[y])*2;
		ans[x]=y;ans[y]=x;
	}
	if(n%2==1) 
	{
		int u=q[n];
		int v=q[1];
		int w=q[1+n/2];
		sum+=dep[u]*2;
		ans[u]=v;
		ans[v]=w;
		ans[w]=u;
//		ans[q[n]]=q[n];sum+=dep[q[n]]*2;
//		swap(ans[1],ans[n]);
	}
	printf("%lld\n",sum);
	for(int i=1;i<=n;i++) printf("%lld ",ans[i]);
	return 0;	
}

最小值是这个:感觉这个还麻烦点,因为需要一个重要结论就是先满足儿子再满足自己。

#include<bits/stdc++.h>
using namespace std;
int n,m,len=0,last[200001],ans[200001],sum=0;
bool v[200001];
struct pp
{
	int x,y,next;
};pp p[400001];
void ins(int x,int y)
{
	int now=++len;
	p[now]={x,y,last[x]};last[x]=now;
	return ; 
}
void dfs(int x,int fa)
{
	for(int i=last[x];i!=-1;i=p[i].next)
	{
		int y=p[i].y; if(y==fa) continue ;
		dfs(y,x);
		if(!ans[x]&&!ans[y]) ans[x]=y,ans[y]=x;
	}
	return ;
}
int main()
{
	memset(last,-1,sizeof(last));memset(v,false,sizeof(v));
	scanf("%d",&n);
	for(int i=1;i<=n-1;i++)
	{
		int x,y;scanf("%d%d",&x,&y);
		ins(x,y);ins(y,x);
	}
	dfs(1,1);
	for(int i=1;i<=n;i++) 
	{
		if(ans[i]) 
		{
			sum++;
			continue ;	
		}
		int tmp=p[last[i]].y;
        ans[i]=ans[tmp];
        ans[tmp]=i;sum+=2;
	}
	printf("%d\n",sum);
	for(int i=1;i<=n;i++) printf("%d ",ans[i]);
	return 0;
}

P3354 [IOI2005]Riv 河流上一题人给我做麻了。这题dp还好说,不想说

#include<bits/stdc++.h>
#define int long long  
using namespace std;
int n,m,K,len=0,last[100001],dep[100001],f[101][101][101],g[101][101][101],wood[100001];
int q[100001],tot=0;
struct pp
{
	int x,y,c,next;
};pp p[1000001];
void ins(int x,int y,int c)
{
	int now=++len;
	p[now]={x,y,c,last[x]};last[x]=now;
	return ;
}
void dfs(int x)
{
	q[++tot]=x; 
	for(int i=last[x];i!=-1;i=p[i].next)
	{
		int y=p[i].y;//if(y==fa) continue ;
		dep[y]=dep[x]+p[i].c;dfs(y);
		for(int j=1;j<=tot;j++)
		{
			for(int k=K;k>=0;k--)
			{
				f[x][q[j]][k]+=f[y][q[j]][0];
				g[x][q[j]][k]+=f[y][x][0];
				for(int kk=0;kk<=k;kk++)
				{
					f[x][q[j]][k]=min(f[x][q[j]][k],f[x][q[j]][k-kk]+f[y][q[j]][kk]);
					g[x][q[j]][k]=min(g[x][q[j]][k],g[x][q[j]][k-kk]+f[y][x][kk]);
				} 
			}
		}
	}	
	for(int i=1;i<=tot;i++)
	{
		for(int k=0;k<=K;k++)	
		{
			if(k>=1) f[x][q[i]][k]=min(f[x][q[i]][k]+wood[x]*(dep[x]-dep[q[i]]),g[x][q[i]][k-1]);
			else f[x][q[i]][k]+=wood[x]*(dep[x]-dep[q[i]]);
		}
	}
	tot--;
	return ;	
}
signed main()
{
	memset(last,-1,sizeof(last));
	scanf("%lld%lld",&n,&K);
	for(int i=1;i<=n;i++)
	{
		int x=i,y,c;scanf("%lld%lld%lld",&wood[i],&y,&c);
		ins(y,x,c);//ins(x,y,c);
	}
	dfs(0);printf("%lld",f[0][0][K]);
	return 0; 
} 

Johnny and Contribution在这里插入图片描述

#include<bits/stdc++.h> 
using namespace std;
int n,m,len=0,last[2000001];
bool v[2000001];
struct pp
{
	int x,y,next;
};pp p[2000001];
struct node
{
	int t,id;
};node e[2000001];
void ins(int x,int y)
{
	int now=++len;
	p[now]={x,y,last[x]};last[x]=now;
	return ;
}
bool cmp(const node &x,const node &y)
{
	return x.t<y.t;
}
int main()
{
	memset(last,-1,sizeof(last));
	scanf("%d%d",&n,&m);int pd=1;
	for(int i=1;i<=m;i++)
	{
		int x,y;scanf("%d%d",&x,&y);
		ins(x,y);ins(y,x); 
	}
	for(int i=1;i<=n;i++) scanf("%d",&e[i].t),e[i].id=i;
	for(int i=1;i<=n;i++)
	{
		memset(v,true,sizeof(v));
		for(int j=last[i];j!=-1;j=p[j].next) v[e[p[j].y].t]=false ;
		for(int j=1;j<e[i].t-1;j++) if(v[j]) printf("-1"),pd=-1;if(pd==-1) break;
		if(!v[e[i].t]) printf("-1"),pd=-1;if(pd==-1) break;
	}
	if(pd==-1) return 0;sort(e+1,e+n+1,cmp);
	for(int i=1;i<=n;i++) printf("%d ",e[i].id);
	return 0;
}

告一段落!
还没有呢!
P5958 [POI2017]Sabotaż

//明显的,因为一开始只有一个点,那么处于叶子节点是最多黑色节点的,考虑树形dp。
//令f[i]表示的是令i不是叛徒的最小的x,再设siz[x]表示子树大小,考虑转移。
//那么令f[i]表示的是令i不是叛徒的最小的x,其实就是令f[i]成为叛徒的最大值 
//那么我要是让一个点变黑色,需要的是要不就是子树的黑嘛?所以我们在(f[y],siz[x]/siz[y])取其小 
//让其满足自己被染黑,还能连带上父亲一起被染。所以直接这样转移就好啦
#include<bits/stdc++.h>
using namespace std;
int n,m,k,len=0,last[500001],siz[500001];double f[500001],ans=0;
struct pp
{
	int x,y,next;
};pp p[1000001];
void ins(int x,int y)
{
	int now=++len;
	p[now]={x,y,last[x]};last[x]=now;
	return ;
}
void dfs(int x,int fa)
{
	siz[x]=1;
	for(int i=last[x];i!=-1;i=p[i].next)
	{
		int y=p[i].y;if(y==fa) continue ;
		dfs(y,x);siz[x]+=siz[y];
	}
	if(siz[x]==1) f[x]=1.0;
	for(int i=last[x];i!=-1;i=p[i].next)
	{
		int y=p[i].y;if(y==fa) continue ;//printf("%lf %lf %lf\n",f[x],f[y],(double)(siz[x]-1)/siz[y]);
		f[x]=max(f[x],min(f[y],(double)(siz[y])/(siz[x]-1)));
	}
	if(siz[x]>k) ans=max(ans,f[x]);//printf("%d %d\n",x,siz[x]);
	return ;
} 
int main()
{
	memset(last,-1,sizeof(last));
	scanf("%d%d",&n,&k);
	for(int i=2;i<=n;i++)
	{
		int x,y;scanf("%d",&x);
		ins(x,i);ins(i,x); 
	}
	dfs(1,1);printf("%.8lf",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值