[noip] 树形dp练习

P1270 “访问”美术馆

裸题。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=2e2+3,M=6e2+3;
int n,cnt,kin[N],f[N][M];
IL int in(){
	char c;int f=1;
	while((c=getchar())<'0'||c>'9')
		if(c=='-') f=-1;
	int x=c-'0';
	while((c=getchar())>='0'&&c<='9')
		x=x*10+c-'0';
	return x*f;
	}
void dfs(int u){
	if(kin[u]) for(int i=0;i<=n;++i) f[u][i]=min(kin[u],i/5);
	else{
		int v=++cnt,x=in();kin[v]=in(),dfs(v);
		for(int i=0;i<=n-2*x;++i) f[u][i+2*x]=f[v][i];
		if(u==1) return;
		v=++cnt,x=in(),kin[v]=in(),dfs(v);
		for(int i=n;i>=2*x;--i)
			for(int j=0;j<=i-2*x;++j)
				f[u][i]=max(f[u][i],f[u][i-j-2*x]+f[v][j]);
		}
	}
int main()
{
	n=in()-1,dfs(++cnt);
	cout<<f[1][n]<<endl;
	return 0;
	}

P2585 [ZJOI2006]三色二叉树

裸题。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=5e5+10;
int n,num,cnt,ls[N],rs[N],f1[N][3],f2[N][3];
char s[N];
IL int in(){
	char c;int f=1;
	while((c=getchar())<'0'||c>'9')
		if(c=='-') f=-1;
	int x=c-'0';
	while((c=getchar())>='0'&&c<='9')
		x=x*10+c-'0';
	return x*f;
	}
void dfs(int i){
	if(s[i]=='0') f1[i][0]=f2[i][0]=1;
	else if(s[i]=='1'){
		dfs(ls[i]=++cnt);
		f1[i][0]=1+min(f1[ls[i]][1],f1[ls[i]][2]);
		f1[i][1]=min(f1[ls[i]][0],f1[ls[i]][2]);
		f1[i][2]=min(f1[ls[i]][0],f1[ls[i]][1]);
		f2[i][0]=1+max(f2[ls[i]][1],f2[ls[i]][2]);
		f2[i][1]=max(f2[ls[i]][0],f2[ls[i]][2]);
		f2[i][2]=max(f2[ls[i]][0],f2[ls[i]][1]);
		}
	else{
		dfs(ls[i]=++cnt),dfs(rs[i]=++cnt);
		f1[i][0]=1+min(f1[ls[i]][1]+f1[rs[i]][2],f1[ls[i]][2]+f1[rs[i]][1]);
		f1[i][1]=min(f1[ls[i]][0]+f1[rs[i]][2],f1[ls[i]][2]+f1[rs[i]][0]);
		f1[i][2]=min(f1[ls[i]][0]+f1[rs[i]][1],f1[ls[i]][1]+f1[rs[i]][0]);
		f2[i][0]=1+max(f2[ls[i]][1]+f2[rs[i]][2],f2[ls[i]][2]+f2[rs[i]][1]);
		f2[i][1]=max(f2[ls[i]][0]+f2[rs[i]][2],f2[ls[i]][2]+f2[rs[i]][0]);
		f2[i][2]=max(f2[ls[i]][0]+f2[rs[i]][1],f2[ls[i]][1]+f2[rs[i]][0]);
	}
}
int main()
{
	scanf("%s",s+1),dfs(cnt=1);
	cout<<max(f2[1][0],max(f2[1][1],f2[1][2]))<<' '<<min(f1[1][0],min(f1[1][1],f1[1][2]))<<endl;
	return 0;
	}

P3174 [HAOI2009]毛毛虫

裸题,将度数转化为点权,求树的直径。

#include<bits/stdc++.h>
#define IL inline
#define M make_pair
#define LL long long
using namespace std;
const int N=3e5+3;
struct hh{
	int to,nxt;
	}e[N<<1];
int n,m,num,fir[N],deg[N],dis[N],vis[N];
map<pair<int,int>,int>mp;
queue<int>q;
IL int in(){
	char c;int f=1;
	while((c=getchar())<'0'||c>'9')
		if(c=='-') f=-1;
	int x=c-'0';
	while((c=getchar())>='0'&&c<='9')
		x=x*10+c-'0';
	return x*f;
	}
IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
int bfs(int s){
	memset(vis,0,sizeof(vis)),dis[s]=0,q.push(s),vis[s]=1;
	while(q.size()){
		int u=q.front();q.pop();
		for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
			if(!vis[v]) vis[v]=1,dis[v]=dis[u]+deg[v]-1,q.push(v);
		}
	int t=1;
	for(int i=2;i<=n;++i) if(dis[t]<dis[i]) t=i;
	return t;
	}
int main()
{
	int x,y,L,R;
	n=in(),m=in();
	for(int i=1;i<n;++i){
		x=in(),y=in();
		if(x>y) swap(x,y);
		if(mp[M(x,y)]) continue;
		add(x,y),add(y,x),mp[M(x,y)]=1,
		++deg[x],++deg[y];
		}
	L=bfs(1),R=bfs(L);
	cout<<dis[R]+deg[L]+1<<endl;
	return 0;
	}

P3177 [HAOI2015]树上染色

发现当子树中黑点个数确定时,其连向父亲的边的对答案的贡献也确定了,并且我们不用关心这些点具体怎么分布。

所以定义 (f_{i,j}) 为子树 (i) 中有 (j) 个黑点时, (i) 中的边对答案的总贡献。

合并即可,复杂度 (O(n^2))

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=2e3+3;
struct hh{
	int to,nxt,w;
	}e[N<<1];
int n,m,num,fir[N],siz[N];
LL f[N][N],g[N];
IL int in(){
	char c;int f=1;
	while((c=getchar())<'0'||c>'9')
		if(c=='-') f=-1;
	int x=c-'0';
	while((c=getchar())>='0'&&c<='9')
		x=x*10+c-'0';
	return x*f;
	}
IL void add(int x,int y,int z){e[++num]=(hh){y,fir[x],z},fir[x]=num;}
void dfs(int u,int fa){
	siz[u]=1;
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
		if(v^fa){
			dfs(v,u);
			for(int j=siz[u];~j;--j)
				for(int k=siz[v];~k;--k) if(j+k<=m)
					f[u][j+k]=max(f[u][j+k],f[u][j]+f[v][k]+1ll*e[i].w*((m-k)*k+(n-m-(siz[v]-k))*(siz[v]-k)));
			siz[u]+=siz[v];
			}
	}
int main()
{
	int x,y,z;
	n=in(),m=in();
	for(int i=1;i<n;++i)
		x=in(),y=in(),z=in(),
		add(x,y,z),add(y,x,z);
	dfs(1,0);
	cout<<f[1][m]<<endl;
	return 0;
	}

P1453 城市环路

基环树裸题。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define LF double
using namespace std;
const int N=1e5+10,inf=1e9;
struct hh{
	int to,nxt;
	}e[N<<1];
int n,m,num=1,fir[N],fa[N],val[N],bo[N<<1],dfn[N],f[N][2],s,t,ans;
LF k;
IL int in(){
	char c;int f=1;
	while((c=getchar())<'0'||c>'9')
		if(c=='-') f=-1;
	int x=c-'0';
	while((c=getchar())>='0'&&c<='9')
		x=x*10+c-'0';
	return x*f;
	}
IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
void find_loop(int u,int f){
	fa[u]=f,dfn[u]=++num;
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
		if(!dfn[v]) find_loop(v,u);
		else if(dfn[v]>dfn[u]){s=u,t=v,bo[i]=bo[i^1]=1;}
  }
void dfs(int u,int fa,int op){
	f[u][0]=0,f[u][1]=val[u];
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
		if(v^fa&&!bo[i])
			dfs(v,u,op),f[u][0]+=max(f[v][0],f[v][1]),f[u][1]+=f[v][0];
	if(op&&u==t) f[u][1]=-inf;
	}
int main()
{
	int x,y,z;
	n=in();
	for(int i=1;i<=n;++i) val[i]=in();
	for(int i=1;i<=n;++i)
		x=in()+1,y=in()+1,
		add(x,y),add(y,x);
	scanf("%lf",&k);
	num=0,find_loop(1,0);
	dfs(s,0,0),ans=f[s][0];
	dfs(s,0,1),ans=max(ans,f[s][1]);
	printf("%.1lf
",ans*k);
	return 0;
	}

CF461B Appleman and Tree

定义 (f_{i,0}) 为在子树 (i) 中,(i) 所处的联通块无黑点的情况数,(f_{i,1}) 为有黑点的情况数。

懒得写了。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define LF double
using namespace std;
const int N=1e5+10,p=1e9+7;
struct hh{
	int to,nxt;
	}e[N<<1];
int n,m,num,fir[N],col[N],f[N][2],fa[N];
IL int in(){
	char c;int f=1;
	while((c=getchar())<'0'||c>'9')
		if(c=='-') f=-1;
	int x=c-'0';
	while((c=getchar())>='0'&&c<='9')
		x=x*10+c-'0';
	return x*f;
	}
IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
IL int ksm(int a,int b){
	int c=1;
	while(b){
		if(b&1) c=1ll*c*a%p;
		a=1ll*a*a%p,b>>=1;
		}
	return c;
	}
IL int mod(int x){return x>=p?x-p:x;}
void dfs(int u){
	int sum=1;
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt) 
		dfs(v),sum=1ll*sum*mod(f[v][0]+f[v][1])%p;
	if(col[u]==1) f[u][1]=sum;
	else{
		f[u][0]=sum;
		for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
			f[u][1]=mod(f[u][1]+1ll*sum*ksm(mod(f[v][0]+f[v][1]),p-2)%p*f[v][1]%p);
		}
	}
int main()
{
	n=in();
	for(int i=2;i<=n;++i) add(fa[i]=in()+1,i);
	for(int i=1;i<=n;++i) col[i]=in();
	dfs(1);cout<<f[1][1]<<endl;
	return 0;
	}

P3647 [APIO2014]连珠线

以最开始的点为根生成树,发现蓝边必然是 (son_u,u,fa_u) 形式的,且点只可能处在一对生成的蓝线中。

定义 (f_{u,0}) 表示子树 (u) 中,且 (u) 不是被 (insert) 生成的最大得分,(f_{u,1}) 为其是由 (insert) 生成的最大得分。

(f_{u,0}=maxlimits_{v in son_u}{(f_{v,0},f_{v,1}+w)})

(f_{u,1}=f_{u,0}+maxlimits_{v in son_u}{(f_{v,0}+w-max{(f_{v,0},f_{v,1}+w)})})

记录使 (f_{u,1}) 取得最大值及次大值的 (v) ,方便换根,换根时注意特判根节点。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=2e5+3,inf=1e9;
struct hh{
	int to,nxt,w;
	}e[N<<1];
int n,m,num,ans,fir[N],f[N][2],g[N][2],h[N][2];
IL int in(){
	char c;int f=1;
	while((c=getchar())<'0'||c>'9')
		if(c=='-') f=-1;
	int x=c-'0';
	while((c=getchar())>='0'&&c<='9')
		x=x*10+c-'0';
	return x*f;
	}
IL void add(int x,int y,int z){e[++num]=(hh){y,fir[x],z},fir[x]=num;}
void dfs1(int u,int fa){
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
		if(v^fa){
			dfs1(v,u),f[u][0]+=max(f[v][0],f[v][1]+e[i].w);
			int res=f[v][0]+e[i].w-max(f[v][0],f[v][1]+e[i].w);
			if(res>h[u][0]) h[u][1]=h[u][0],h[u][0]=res;
			else if(res>h[u][1]) h[u][1]=res;
			}
	f[u][1]=f[u][0]+h[u][0];
	}
void dfs2(int u,int fa,int w){
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
		if(v^fa){
			int Max=max(f[v][0],f[v][1]+e[i].w),M=max(g[u][0],g[u][1]+w);
			g[v][0]=f[u][0]-Max+M;
			if(f[v][0]+e[i].w-Max==h[u][0])
				g[v][1]=g[v][0]+max(h[u][1],u==1?-inf:g[u][0]+w-M);
			else g[v][1]=g[v][0]+max(h[u][0],u==1?-inf:g[u][0]+w-M);
			dfs2(v,u,e[i].w);
			}
	ans=max(ans,f[u][0]+max(g[u][0],g[u][1]+w));
	}
int main()
{
	memset(h,-63,sizeof(h));
	int x,y,z;
	n=in();
	for(int i=1;i<n;++i)
		x=in(),y=in(),z=in(),
		add(x,y,z),add(y,x,z);
	dfs1(1,0),dfs2(1,0,0);
	cout<<ans<<endl;
	return 0;
	}

P3523 [POI2011]DYN-Dynamite

不会啊。。。我太菜了呜呜呜。。。

很明显,外层要套个二分。

定义 (f_u)(u) 子树中,未被覆盖的关键点与 (u) 距离的最大值,(g_u)(u) 子树中选择的点距离 (u) 的最小值,贪心求解。

(f_u=maxlimits_{v in u}{f_v+1})

(g_u=minlimits_{v in u}{g_v+1})

初值为 (f_u=-inf)(g_u=inf)

先考虑若 (u) 为关键点时,(f_u=max{(f_u,0)})

(f_u+g_u<=mid),则之前选择的点可以覆盖 (u) 中的关键点,(f_u=-inf)

否则若 (f_u=mid),则 (u) 必须被选择了,计数器加 (1)

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=3e5+3,inf=0x3f3f3f3f;
struct hh{
  int to,nxt;
}e[N<<1];
int n,m,num,fir[N],f[N],g[N],bo[N],tot,d;
IL int in(){
  char c;int f=1;
  while((c=getchar())<'0'||c>'9')
    if(c=='-') f=-1;
  int x=c-'0';
  while((c=getchar())>='0'&&c<='9')
    x=x*10+c-'0';
  return x*f;
}
IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
void dfs(int u,int fa){
  for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
    if(v^fa){
		  dfs(v,u);
		  f[u]=max(f[u],f[v]+1),
		  g[u]=min(g[u],g[v]+1);
		}
	if(bo[u]&&g[u]>d) f[u]=max(0,f[u]);
	if(f[u]+g[u]<=d) f[u]=-inf;
	if(f[u]==d) f[u]=-inf,g[u]=0,++tot;
}
int pd(){
	tot=0;
	memset(f,-63,sizeof(f));
	memset(g,63,sizeof(g));
  dfs(1,0);
  tot+=f[1]>=0;
  return tot<=m;
}
int main()
{
	int x,y;
	n=in(),m=in();
	for(int i=1;i<=n;++i) bo[i]=in();
	for(int i=1;i<n;++i)
	  x=in(),y=in(),
	  add(x,y),add(y,x);
	int l=0,r=n;
	while(l<r){
	  d=l+r>>1;
	  if(pd()) r=d;
	  else l=d+1;
	}
	cout<<l<<endl;
  return 0;
}

P3554 [POI2013]LUK-Triumphal arch

裸题。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=3e5+3;
struct hh{
  int to,nxt;
}e[N<<1];
int n,m,k,num,f[N],fir[N];
IL int in(){
  char c;int f=1;
  while((c=getchar())<'0'||c>'9')
    if(c=='-') f=-1;
  int x=c-'0';
  while((c=getchar())>='0'&&c<='9')
    x=x*10+c-'0';
  return x*f;
}
IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
void dfs(int u,int fa){
	f[u]=-k;
  for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
    if(v^fa) dfs(v,u),f[u]+=f[v]+1;
  if(f[u]<0) f[u]=0;
}
int pd(){
  dfs(1,0);return !f[1];
}
int main()
{
	int x,y;
	n=in();
	for(int i=1;i<n;++i)
	  x=in(),y=in(),
	  add(x,y),add(y,x);
	int l=0,r=n;
	while(l<r){
	  k=l+r>>1;
	  if(pd()) r=k;
	  else l=k+1;
	}
	cout<<l<<endl;
  return 0;
}

P3565 [POI2014]HOT-Hotels

显然三个点中,必有两个点深度相同,三点到深度相同的两点的 (lca) 的距离相等。

于是定义 (f_{i,j}) 第三个点与 (i) 的距离应该为 (j) 的方案个数,(g_{i,j})(i) 子树中,到 (i) 距离为 (j) 的结点个数。

枚举子树转移即可。

但这题卡空间。。。

注意到边权为 (1),可以用长链剖分优化,注意 (f) 数组要开两倍,复杂度 (O(n))

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=5e3+3;
struct hh{
  int to,nxt;
}e[N<<1];
int n,num,fir[N],dep[N],d[N],son[N],s[N];
int *f[N],*g[N],p1[N<<1],p2[N],*o1=p1,*o2=p2;
LL ans;
IL int in(){
  char c;int f=1;
  while((c=getchar())<'0'||c>'9')
    if(c=='-') f=-1;
  int x=c-'0';
  while((c=getchar())>='0'&&c<='9')
    x=x*10+c-'0';
  return x*f;
}
IL void link(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
void dfs1(int u,int fa){
	dep[u]=dep[fa]+1;
  for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
    if(v^fa){
		  dfs1(v,u);
		  if(d[v]>d[son[u]]) son[u]=v;
		}
	d[u]=d[son[u]]+1;
}
void dfs2(int u,int fa){
	if(son[u]) f[son[u]]=f[u]-1,g[son[u]]=g[u]+1,dfs2(son[u],u);
	g[u][0]=1,ans+=f[u][0];
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
	  if(v^fa&&v^son[u]){
		  o1+=d[v],f[v]=o1,o1+=d[v],g[v]=o2,o2+=d[v],dfs2(v,u);
		  for(int j=0;j<d[v];++j){
			  ans+=1ll*f[u][j+1]*g[v][j];
			  if(j) ans+=1ll*g[u][j-1]*f[v][j];
			}
			for(int j=0;j<d[v];++j){
				f[u][j+1]+=g[u][j+1]*g[v][j];
			  if(j) f[u][j-1]+=f[v][j];
			  g[u][j+1]+=g[v][j];
			}
		}
}
int main()
{
	int x,y;
	n=in();
	for(int i=1;i<n;++i)
	  x=in(),y=in(),
	  link(x,y),link(y,x);
	dfs1(1,0),o1+=d[1],f[1]=o1,o1+=d[1],g[1]=o2,o2+=d[1],dfs2(1,0);
	printf("%lld
",ans);
  return 0;
}

P3574 [POI2014]FAR-FarmCraft

转移贪心,按 (f_u-2(dis_u+1)) 降序排序,微扰法证明。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=5e5+3;
struct hh{
  int to,nxt;
}e[N<<1];
struct kk{
  int id;LL val;
  bool operator<(const kk &a) const{
	return val>a.val;}
}a[N];
int n,num,fir[N],dis[N];
LL f[N],c[N];
IL int in(){
  char c;int f=1;
  while((c=getchar())<'0'||c>'9')
    if(c=='-') f=-1;
  int x=c-'0';
  while((c=getchar())>='0'&&c<='9')
    x=x*10+c-'0';
  return x*f;
}
IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
void dfs(int u,int fa){
  for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
    if(v^fa) dfs(v,u),dis[u]+=dis[v]+1;
  int cnt=0,sum=0;
  for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
    if(v^fa) a[++cnt]=(kk){v,f[v]-2*(dis[v]+1)};
  sort(a+1,a+cnt+1);
  for(int i=1;i<=cnt;++i) ++sum,f[u]=max(f[u],f[a[i].id]+sum),sum+=2*dis[a[i].id]+1;
  if(u^1) f[u]=max(c[u],f[u]);
  else f[u]=max(c[u]+2*dis[u],f[u]);
}
int main()
{
	int x,y;
	n=in();
	for(int i=1;i<=n;++i) c[i]=in();
	for(int i=1;i<n;++i)
	  x=in(),y=in(),
	  add(x,y),add(y,x);
	dfs(1,0);printf("%lld
",f[1]);
  return 0;
}

P3576 [POI2014]MRO-Ant colony

裸题,处理时依关键边分成两棵树。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=1e6+3;
LL inf=1e9+10;
struct hh{
  int to,nxt;
}e[N<<1];
int n,m,k,num,s1,s2,fir[N],deg[N];
LL ans,g[N],dp[N];
IL int in(){
  char c;int f=1;
  while((c=getchar())<'0'||c>'9')
    if(c=='-') f=-1;
  int x=c-'0';
  while((c=getchar())>='0'&&c<='9')
    x=x*10+c-'0';
  return x*f;
}
IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
void dfs(int u,int f){
	dp[u]=min(inf,dp[f]*(deg[u]-1));
	if(deg[u]==1){
	  LL vl=dp[f]*k,vr=dp[f]*(k+1)-1;
	  int l=lower_bound(g+1,g+m+1,vl)-g,r=upper_bound(g+1,g+m+1,vr)-g-1;
	  ans+=1ll*(r-l+1)*k;
	}
  for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
    if(v^f) dfs(v,u);
}
int main()
{
	int x,y;
	n=in(),m=in(),k=in();
	for(int i=1;i<=m;++i) g[i]=in();
	sort(g+1,g+m+1);
	for(int i=1;i<n;++i){
	  x=in(),y=in(),
		++deg[x],++deg[y],
	  add(x,y),add(y,x);
	  if(i==1) s1=x,s2=y;
	}
	dp[s2]=1,dfs(s1,s2);
	dp[s1]=1,dfs(s2,s1);
	printf("%lld
",ans);
  return 0;
}

P4099 [HEOI2013]SAO

不会啊。。。

定义 (f_{u,i})(u) 在其子树中排名为 (i) 的方案数。

考虑合并 (f_{v,k})

(v)(u) 前,枚举 (v) 中有 (j) 个点在 (u) 前,组合数转移。

因为至少有 (k) 个点在 (u) 前,所以枚举时 (1 leq k leq j)

(v)(i) 后,枚举 (v) 中有 (j) 个点在 (u) 前,组合数转移。

因为至多有 (k-1) 个点在 (u) 前,所以枚举时 (j+1 leq k leq siz_v)

(f_v) 求前缀和优化一维,复杂度 (O(n^2))

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=1e3+3,p=1e9+7;
struct hh{
  int to,nxt,bo;
}e[N<<1];
int n,m,num,fir[N],f[N][N],g[N],siz[N],c[N][N];
IL int in(){
  char c;int f=1;
  while((c=getchar())<'0'||c>'9')
    if(c=='-') f=-1;
  int x=c-'0';
  while((c=getchar())>='0'&&c<='9')
    x=x*10+c-'0';
  return x*f;
}
IL void add(int x,int y,int z){e[++num]=(hh){y,fir[x],z},fir[x]=num;}
IL int mod(int x){return x>=p?x-p:x;}
void dfs(int u,int fa){
	siz[u]=1,f[u][1]=1;
  for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
    if(v^fa){
		  dfs(v,u),memset(g,0,sizeof(g));
		  if(e[i].bo){
			  for(int j=1;j<=siz[u];++j)
			    for(int k=0;k<siz[v];++k)
			      g[j+k]=mod(g[j+k]+1ll*c[j+k-1][k]*c[siz[u]+siz[v]-j-k][siz[u]-j]%p*mod(f[v][siz[v]]-f[v][k]+p)%p*f[u][j]%p);
			}
			else{
			  for(int j=1;j<=siz[u];++j)
			    for(int k=1;k<=siz[v];++k)
			      g[j+k]=mod(g[j+k]+1ll*c[j+k-1][k]*c[siz[u]+siz[v]-j-k][siz[u]-j]%p*f[v][k]%p*f[u][j]%p);
			}
			siz[u]+=siz[v],memcpy(f[u],g,sizeof(g));
		}
	for(int i=1;i<=siz[u];++i) f[u][i]=mod(f[u][i]+f[u][i-1]);
}
void solve(){
	int x,y;char s[10];
	memset(fir,num=0,sizeof(fir)),
	memset(f,0,sizeof(f));
  n=in();
  for(int i=1;i<n;++i)
	  x=in()+1,scanf("%s",s+1),y=in()+1,
	  add(x,y,s[1]=='<'),add(y,x,s[1]=='>');
	dfs(1,0);
	printf("%d
",f[1][n]);
}
void pre(int n){
  for(int i=0;i<=n;++i){
	  c[i][0]=1;
	  for(int j=1;j<=i;++j)
	    c[i][j]=mod(c[i-1][j]+c[i-1][j-1]);
	}
}
int main()
{	
  int T=in();
  pre(1e3);
  while(T--) solve();
  return 0;
}

P4253 [SCOI2015]小凸玩密室

若以 (u) 开始,遍历完子树后,最后的点只可能是在叶子中。

所以定义开一个数组 (q),表示 (u) 中最后的点在哪个叶子时花费的最小值。

因为是完全二叉树,所以空间为 (O(nlog n))

枚举左右子树转移即可。

最后再遍历一遍树,按 (q) 开一个 (f) 数组统计答案,注意 (u) 子树中最后点可能是在叶子也可能在 (u)

复杂度 (O(nlog n))

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=2e5+3;
const LL inf=1e18;
struct hh{
  LL dis,val;
}*q[N],*f[N];
int n,m,ls[N],rs[N],lw[N],rw[N],siz[N],val[N];
LL ans=inf;
IL int in(){
  char c;int f=1;
  while((c=getchar())<'0'||c>'9')
    if(c=='-') f=-1;
  int x=c-'0';
  while((c=getchar())>='0'&&c<='9')
    x=x*10+c-'0';
  return x*f;
}
void dfs1(int u){
  if(ls[u]) dfs1(ls[u]);
  if(rs[u]) dfs1(rs[u]);
  siz[u]=siz[ls[u]]+siz[rs[u]];
  if(!ls[u]&&!rs[u]) siz[u]=1;
  q[u]=new hh [siz[u]+1];
  f[u]=new hh [siz[u]+1];
  if(!ls[u]&&!rs[u]) q[u][1]=(hh){0,0};
  else if(!rs[u]){
	  for(int i=1;i<=siz[ls[u]];++i)
		  q[u][i]=(hh){q[ls[u]][i].dis+lw[u],q[ls[u]][i].val+1ll*lw[u]*val[ls[u]]};
	}
	else{
		LL lm=inf,rm=inf;
		for(int i=1;i<=siz[rs[u]];++i)
	    rm=min(rm,q[rs[u]][i].val+(q[rs[u]][i].dis+lw[u]+rw[u])*val[ls[u]]+1ll*rw[u]*val[rs[u]]);
	  for(int i=1;i<=siz[ls[u]];++i) q[u][i]=(hh){q[ls[u]][i].dis+lw[u],rm+q[ls[u]][i].val};
	  for(int i=1;i<=siz[ls[u]];++i)
	    lm=min(lm,q[ls[u]][i].val+(q[ls[u]][i].dis+lw[u]+rw[u])*val[rs[u]]+1ll*lw[u]*val[ls[u]]);
	  for(int i=1;i<=siz[rs[u]];++i) q[u][i+siz[ls[u]]]=(hh){q[rs[u]][i].dis+rw[u],lm+q[rs[u]][i].val};
	}
}
void dfs2(int u){
	if(ls[u]) dfs2(ls[u]);
	if(rs[u]) dfs2(rs[u]);
  if(!ls[u]&&!rs[u]) f[u][0]=f[u][1]=(hh){0,0};
  else if(!rs[u]){
	  LL Min=inf;
	  for(int i=0;i<=siz[ls[u]];++i)
	    Min=min(Min,f[ls[u]][i].val+1ll*(f[ls[u]][i].dis+lw[u])*val[u]);
	  f[u][0]=(hh){0,Min};
	  for(int i=1;i<=siz[u];++i) f[u][i]=q[u][i];
	}
	else{
	  LL lm=inf,rm=inf;f[u][0]=(hh){0,inf};
	  for(int i=1;i<=siz[u];++i) f[u][i]=q[u][i];
	  for(int i=0;i<=siz[rs[u]];++i)
	    rm=min(rm,f[rs[u]][i].val+1ll*(f[rs[u]][i].dis+rw[u])*val[u]+1ll*lw[u]*val[ls[u]]);
	  for(int i=1;i<=siz[ls[u]];++i)
	    if(q[ls[u]][i].val+rm<f[u][i].val)
	      f[u][i].val=q[ls[u]][i].val+rm;
	  for(int i=0;i<=siz[ls[u]];++i)
	    lm=min(lm,f[ls[u]][i].val+1ll*(f[ls[u]][i].dis+lw[u])*val[u]+1ll*rw[u]*val[rs[u]]);
	  for(int i=1;i<=siz[rs[u]];++i)
	    if(q[rs[u]][i].val+lm<f[u][i+siz[ls[u]]].val)
	      f[u][i+siz[ls[u]]].val=q[rs[u]][i].val+lm;
	}
}
int main()
{
	n=in();
	for(int i=1;i<=n;++i) val[i]=in();
	for(int i=2;i<=n;++i)
	  if(i&1) rs[i>>1]=i,rw[i>>1]=in();
	  else ls[i>>1]=i,lw[i>>1]=in();
	dfs1(1),dfs2(1);
	for(int i=0;i<=siz[1];++i) ans=min(ans,f[1][i].val);
	printf("%lld
",ans);
  return 0;
}

P6213 「SWTR-04」Collecting Coins

被卡常惹 (QwQ)。。。

假设到了点 (u),显然只能再走 (k_u-1) 条边,于是转移时排序,注意儿子的子树中有 (d) 的话优先级是最大的。

再换根,注意先处理出能走出迷宫的点,只用它们的数据更新。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define pb push_back
using namespace std;
const int N=1e5+3;
struct hh{
  int to,nxt,w;
}e[N<<1];
int n,d,m,num,fir[N],s[N],st[N],ed[N],f[N],g[N],bo[N],ans;
struct kk{
  int id,val,bo;
  bool operator<(const kk &a) const{
	  if(bo) return 1;if(a.bo) return 0;
	  return val>a.val;
	}
};
vector<kk>q[N];
IL int in(){
  char c;int f=1;
  while((c=getchar())<'0'||c>'9')
    if(c=='-') f=-1;
  int x=c-'0';
  while((c=getchar())>='0'&&c<='9')
    x=x*10+c-'0';
  return x*f;
}
IL int bel(int x){return st[d]>=st[x]&&st[d]<=ed[x];}
IL void add(int x,int y,int z){e[++num]=(hh){y,fir[x],z},fir[x]=num;}
void dfs0(int u,int fa){
	bo[u]=1;
  for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
    if(v^fa&&(u==d||s[u]>=2)) dfs0(v,u);
}
void dfs1(int u,int fa){
	st[u]=++num;
  for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
    if(v^fa) dfs1(v,u),q[u].pb((kk){v,f[v]+e[i].w,bel(v)});
  ed[u]=num;
  sort(q[u].begin(),q[u].end());
  for(int i=0;i<q[u].size()&&i<s[u]-1;++i) f[u]+=q[u][i].val;
}
void query(int u){
	int res=0;
  for(int i=0;i<q[u].size()&&i<s[u]-1;++i) res+=q[u][i].val;
  ans=max(ans,res);
}
void dfs2(int u,int fa,int w){
	if(!q[u].size()){if(s[u]^1&&bo[u]) ans=max(ans,g[u]+w);return;}
	int v=q[u][0].id;q[u].pb((kk){0,g[u]+w,!bel(v)&&(u^d)});
	sort(q[u].begin(),q[u].end());
  for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
    if(v^fa){
		  for(int j=0,k=1;j<q[u].size()&&k<s[u];++j)
		    if(q[u][j].id^v) g[v]+=q[u][j].val,++k;
		  dfs2(v,u,e[i].w);
		}
	if(bo[u]) query(u);
}
int main()
{
	int x,y,z;
	n=in(),d=in();
	for(int i=1;i<n;++i)
	  x=in(),y=in(),z=in(),
	  add(x,y,z),add(y,x,z);
	for(int i=1;i<=n;++i) s[i]=in();
	num=0,dfs0(d,0),dfs1(1,0),dfs2(1,0,0);
	printf("%d
",ans);
  return 0;
}

P6419 [COCI2014-2015#1] Kamp

裸题。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=5e5+3;
struct hh{
 int to,nxt,w;
}e[N<<1];
int n,m,num,fir[N],b1[N],b2[N],b3[N];
LL f1[N],g1[N],f2[N],g2[N],p[N][2],ans[N];
IL int in(){
  char c;int f=1;
  while((c=getchar())<'0'||c>'9')
    if(c=='-') f=-1;
  int x=c-'0';
  while((c=getchar())>='0'&&c<='9')
    x=x*10+c-'0';
  return x*f;
}
IL void add(int x,int y,int z){e[++num]=(hh){y,fir[x],z},fir[x]=num;}
void dfs1(int u,int fa){
	b2[u]=b1[u];
  for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
    if(v^fa){
		  dfs1(v,u),b2[u]|=b2[v];
		  if(b2[v]){
			  f1[u]+=f1[v]+2*e[i].w;
			  if(f1[v]-g1[v]+e[i].w>p[u][0]) p[u][1]=p[u][0],p[u][0]=f1[v]-g1[v]+e[i].w;
			  else if(f1[v]-g1[v]+e[i].w>p[u][1]) p[u][1]=f1[v]-g1[v]+e[i].w;
			}
		}
	g1[u]=f1[u]-p[u][0];
}
void dfs2(int u,int fa,int w){
	int c=0,to=0;
	if(b3[u]) ans[u]=f1[u]+f2[u]+2*w-max(p[u][0],f2[u]-g2[u]+w);
	else ans[u]=g1[u];
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
	  if(v^fa&&b2[v]) ++c,to=v;
	if(b1[u]) c=2;if(b3[u]) ++c,to=fa;
  for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
    if(v^fa){
		  if((c>=2||to^v)&&c){
		  	b3[v]=1;LL x=0;if(b3[u]) x=f2[u]-g2[u]+w;
		  	f2[v]=f1[u]-f1[v]-2*b2[v]*e[i].w+f2[u]+2*b3[u]*w;
			  if(f1[v]-g1[v]+e[i].w==p[u][0]) g2[v]=f2[v]-max(p[u][1],x);
			  else g2[v]=f2[v]-max(p[u][0],x);
			}
			dfs2(v,u,e[i].w);
		}
}
int main()
{
	int x,y,z;
	n=in(),m=in();
	for(int i=1;i<n;++i)
	  x=in(),y=in(),z=in(),
	  add(x,y,z),add(y,x,z);
	for(int i=1;i<=m;++i) b1[in()]=1;
	dfs1(1,0),dfs2(1,0,0);
	for(int i=1;i<=n;++i) printf("%lld
",ans[i]);
  return 0;
}

P4516 [JSOI2018]潜入行动

背包,设计好状态式子挺好推的。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define re register
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
using namespace std;
const int N=1e5+3,K=1e2+3,p=1e9+7;
struct hh{
  int to,nxt;
}e[N<<1];
int n,m,num,siz[N],fir[N],f[N][K][2][2],g[K][2][2];
char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
IL int in(){
  char c;int f=1;
  while((c=getchar())<'0'||c>'9')
    if(c=='-') f=-1;
  int x=c-'0';
  while((c=getchar())>='0'&&c<='9')
    x=x*10+c-'0';
  return x*f;
}
IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
IL int mod(int x){return x>=p?x-p:x;}
void dfs(int u,int fa){
	siz[u]=f[u][0][0][0]=f[u][1][1][0]=1;
  for(re int i=fir[u],v;v=e[i].to;i=e[i].nxt)
    if(v^fa){
		  dfs(v,u),siz[u]+=siz[v];
		  for(re int j=min(m,siz[u]);~j;--j)
		    g[j][0][0]=g[j][1][0]=g[j][0][1]=g[j][1][1]=0;
		  for(re int j=min(m,siz[u]);~j;--j)
		    for(re int k=min(j,siz[v]);~k;--k){
					if(f[u][j-k][0][0]){
					  g[j][0][0]=mod(g[j][0][0]+1ll*f[u][j-k][0][0]*f[v][k][0][1]%p),
					  g[j][0][1]=mod(g[j][0][1]+1ll*f[u][j-k][0][0]*f[v][k][1][1]%p); 
					}
					if(f[u][j-k][0][1]){
					  g[j][0][1]=mod(g[j][0][1]+1ll*f[u][j-k][0][1]*mod(f[v][k][0][1]+f[v][k][1][1])%p);
					}
					if(f[u][j-k][1][0]){
						g[j][1][0]=mod(g[j][1][0]+1ll*f[u][j-k][1][0]*mod(f[v][k][0][0]+f[v][k][0][1])%p),
						g[j][1][1]=mod(g[j][1][1]+1ll*f[u][j-k][1][0]*mod(f[v][k][1][0]+f[v][k][1][1])%p);
					}
					if(f[u][j-k][1][1]){
						g[j][1][1]=mod(g[j][1][1]+1ll*f[u][j-k][1][1]*((1ll*f[v][k][0][0]+f[v][k][0][1]+f[v][k][1][0]+f[v][k][1][1])%p)%p);
					}
				}
			for(re int j=min(m,siz[u]);~j;--j)
			  f[u][j][0][0]=g[j][0][0],
			  f[u][j][1][0]=g[j][1][0],
			  f[u][j][0][1]=g[j][0][1],
			  f[u][j][1][1]=g[j][1][1];
		}
}
int main()
{
	int x,y;
	n=in(),m=in();
	for(re int i=1;i<n;++i)
	  x=in(),y=in(),
	  add(x,y),add(y,x);
	dfs(1,0);
	printf("%d
",mod(f[1][m][0][1]+f[1][m][1][1]));
  return 0;
}

P3354 [IOI2005]Riv 河流

数组将其祖先最近的伐木场也存下来,方便计算代价。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=1e2+3;
struct hh{
  int to,nxt,w;
}e[N<<1];
int n,m,k,num,fir[N],f[N][N][N],g[N][N][N],w[N],sta[N],dis[N];
IL int in(){
  char c;int f=1;
  while((c=getchar())<'0'||c>'9')
    if(c=='-') f=-1;
  int x=c-'0';
  while((c=getchar())>='0'&&c<='9')
    x=x*10+c-'0';
  return x*f;
}
IL void add(int x,int y,int z){e[++num]=(hh){y,fir[x],z},fir[x]=num;}
void dfs(int u){
  sta[++sta[0]]=u;
  for(int i=fir[u],v;v=e[i].to;i=e[i].nxt){
	  dis[v]=dis[u]+e[i].w,dfs(v);
	  for(int j=1;j<=sta[0];++j)
	    for(int k=m;~k;--k){
			  f[u][sta[j]][k]+=f[v][sta[j]][0],
			  g[u][sta[j]][k]+=f[v][u][0];
			  for(int l=1;l<=k;++l)
			    f[u][sta[j]][k]=min(f[u][sta[j]][k],f[u][sta[j]][k-l]+f[v][sta[j]][l]),
			    g[u][sta[j]][k]=min(g[u][sta[j]][k],g[u][sta[j]][k-l]+f[v][u][l]);
			}
	}
	for(int i=1;i<=sta[0];++i){
	  for(int j=1;j<=m;++j)
	    f[u][sta[i]][j]=min(f[u][sta[i]][j]+w[u]*(dis[u]-dis[sta[i]]),g[u][sta[i]][j-1]);
	  f[u][sta[i]][0]+=w[u]*(dis[u]-dis[sta[i]]);
	}
	--sta[0]; 
}
int main()
{
	int x,y,z;
	n=in(),m=in();
	for(int i=1;i<=n;++i)
	  w[i]=in(),x=in(),y=in(),
	  add(x,i,y);
	dfs(0);
	cout<<f[0][0][m]<<endl;
  return 0;
}
原文地址:https://www.cnblogs.com/yiqiAtiya/p/13968108.html