• [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;
    }
    
  • 相关阅读:
    关于ViewPager、ViewFilpper、ViewFlow三种实现水平向滑动方式的比较
    php 使用curl模拟登录discuz以及模拟发帖
    php 使用curl模拟登录人人(校内)网
    PHP PDO函数库详解
    比CMD更强大的命令行WMIC
    强大的命令行工具wmic
    wmic 命令的一个汇总,功能很强大
    Oracle 11g default profile 默认启用密码过期180天 ORA-28001错误处理
    Oracle 11g密码过期问题解决方案
    SQLPlus 在连接时通常有四种方式
  • 原文地址:https://www.cnblogs.com/yiqiAtiya/p/13968108.html
Copyright © 2020-2023  润新知