@ysner
2018-08-13T16:35:36.000000Z
字数 1464
阅读 2589
DP 树形DP
给定一个个点条边的无向无环图,在尽量少的节点上放灯,使得所有边都与灯相邻(被灯照亮)。
在灯的总数最小的前提下,被两盏灯同时照亮的边数应该尽可能大。
有一种套路,如果要同时最小化或最大化两个量,则等价于最小化或最大化。
并且,必须大到足以区分。一般来说,。
所以本题可以设。
但是,题目要求是最小化,最大化???
可以转化一下,把表示为“只被一盏灯照亮的边数”(因为)。
于是设分别表示以为根的子树内,不放灯和放灯对答案()的贡献。
转移时不统计被两盏灯同时照亮的边的贡献。
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<cmath>#include<algorithm>#include<vector>#define re register#define il inline#define ll long long#define max(a,b) ((a)>(b)?(a):(b))#define min(a,b) ((a)<(b)?(a):(b))#define fp(i,a,b) for(re int i=a;i<=b;i++)#define fq(i,a,b) for(re int i=a;i>=b;i--)using namespace std;const int mod=1e9+7,N=2000,M=3000;struct Edge{int to,nxt;}e[N<<1];int n,m,h[N],cnt,dp[N][2],vis[N];il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u]};h[u]=cnt;}ll ans=0;il ll gi(){re ll x=0,t=1;re char ch=getchar();while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();if(ch=='-') t=-1,ch=getchar();while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();return x*t;}il void dfs(re int u,re int fa){vis[u]=1;dp[u][0]=0;dp[u][1]=M;for(re int i=h[u];i+1;i=e[i].nxt){re int v=e[i].to;if(v==fa||vis[v]) continue;dfs(v,u);dp[u][0]+=dp[v][1]+1;dp[u][1]+=min(dp[v][0]+1,dp[v][1]);}}int main(){re int T=gi();while(T--){memset(h,-1,sizeof(h));cnt=0;n=gi();m=gi();ans=0;memset(vis,0,sizeof(vis));fp(i,1,m){re int u=gi()+1,v=gi()+1;add(u,v);add(v,u);}fp(i,1,n)if(!vis[i]) dfs(i,0),ans+=min(dp[i][0],dp[i][1]);printf("%lld %lld %lld\n",ans/M,m-ans%M,ans%M);}return 0;}
