https://zybuluo.com/ysner/note/1248929
题面
给定一个(n)个点(m)条边的无向无环图,在尽量少的节点上放灯,使得所有边都与灯相邻(被灯照亮)。
在灯的总数最小的前提下,被两盏灯同时照亮的边数应该尽可能大。
- (n,mleq1000)
题面
有一种套路,如果要同时最小化或最大化两个量(a,b),则等价于最小化或最大化(aM+b)。
并且,(M)必须大到足以区分(a,b)。一般来说,(M>max{abs(a-b)})。
所以本题可以设(M=3000)。
但是,题目要求是最小化(a),最大化(b)???
可以转化一下,把(b)表示为“只被一盏灯照亮的边数”(因为(b'=m-b))。
于是设(f[i][0/1])分别表示以(i)为根的子树内,不放灯和放灯对答案((aM+b))的贡献。
转移时不统计被两盏灯同时照亮的边的贡献。
#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
",ans/M,m-ans%M,ans%M);
}
return 0;
}