题目
点这里看题目。
分析
题目本身不难想,只需要在正常的 DP 外多加一个辅助状态即可求解附加问题。
这个方法就不细讲了。重点说的是一个比较优雅的做法。
考虑我们要最大化被两端覆盖的边的数量,也即是最小化只被一端覆盖的边的数量。现在我们就是同时最小化两个信息,并且存在优先级。
思考一下哪里存在天然的优先级?**数位!**如果我们把灯的数量压入高位,一端覆盖的边压入低位,对状态取 (min) 的时候,我们自然而然地满足了灯数量的优先。
很巧妙吧。听到这个做法的时候,我也这样认为。
具体而言,我们可以找到一个大于 (m) 的数 (K) ,并且将信息压成 (K) 进制的两位数,其中高位存灯、低位存边,然后就有:
(f(u,0/1)):经过信息压缩的意义不变的状态。
此时对状态$pm K$就相当于修改灯的数量,$pm1$就相当于修改边的数量。
然后存在转移:
[
egin{aligned}
f(u,0)&=sum_v f(v,1)+1\
f(u,1)&=K+sum_v min{f(v,0)+1,f(v,1)}
end{aligned}
]
有意思吧。
代码
粗暴版本
#include <cstdio>
const int MAXN = 1e5 + 5, MAXM = 1e5 + 5;
template<typename _T>
void read( _T &x )
{
x = 0;char s = getchar();int f = 1;
while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
x *= f;
}
template<typename _T>
void write( _T x )
{
if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
if( 9 < x ){ write( x / 10 ); }
putchar( x % 10 + '0' );
}
template<typename _T>
_T MAX( const _T a, const _T b )
{
return a > b ? a : b;
}
struct edge
{
int to, nxt;
}Graph[MAXN << 1];
int f[MAXN][2], g[MAXN][2];
int head[MAXN];
int N, M, cnt;
bool vis[MAXN];
void addEdge( const int from, const int to )
{
Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
head[from] = cnt;
}
void DFS( const int u, const int fa )
{
vis[u] = true;
f[u][0] = 0, f[u][1] = 1;
g[u][0] = g[u][1] = 0;
for( int i = head[u], v ; i ; i = Graph[i].nxt )
if( ( v = Graph[i].to ) ^ fa )
{
DFS( v, u );
f[u][0] += f[v][1], g[u][0] += g[v][1];
if( f[v][1] < f[v][0] ) f[u][1] += f[v][1], g[u][1] += g[v][1] + 1;
else if( f[v][1] > f[v][0] ) f[u][1] += f[v][0], g[u][1] += g[v][0];
else f[u][1] += f[v][1], g[u][1] += MAX( g[v][1] + 1, g[v][0] );
}
}
void clr()
{
cnt = 0;
for( int i = 1 ; i <= N ; i ++ )
vis[i] = head[i] = 0;
}
int main()
{
int T;
read( T );
while( T -- )
{
int ans1 = 0, ans2 = 0;
read( N ), read( M ), clr();
for( int i = 1, a, b ; i <= M ; i ++ )
read( a ), read( b ), a ++, b ++,
addEdge( a, b ), addEdge( b, a );
for( int i = 1 ; i <= N ; i ++ )
if( ! vis[i] )
{
DFS( i, 0 );
if( f[i][0] < f[i][1] ) ans1 += f[i][0], ans2 += g[i][0];
else if( f[i][0] > f[i][1] ) ans1 += f[i][1], ans2 += g[i][1];
else ans1 += f[i][0], ans2 += MAX( g[i][0], g[i][1] );
}
write( ans1 ), putchar( ' ' ), write( ans2 ), putchar( ' ' ), write( M - ans2 ), putchar( '
' );
}
return 0;
}
优雅版本
#include <cstdio>
const int K = 1e3;
const int MAXN = 1e5 + 5, MAXM = 1e5 + 5;
template<typename _T>
void read( _T &x )
{
x = 0;char s = getchar();int f = 1;
while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
x *= f;
}
template<typename _T>
void write( _T x )
{
if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
if( 9 < x ){ write( x / 10 ); }
putchar( x % 10 + '0' );
}
template<typename _T>
_T MIN( const _T a, const _T b )
{
return a < b ? a : b;
}
struct edge
{
int to, nxt;
}Graph[MAXN << 1];
int f[MAXN][2];
int head[MAXN];
int N, M, cnt;
bool vis[MAXN];
void addEdge( const int from, const int to )
{
Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
head[from] = cnt;
}
void DFS( const int u, const int fa )
{
vis[u] = true;
f[u][0] = 0, f[u][1] = K;
for( int i = head[u], v ; i ; i = Graph[i].nxt )
if( ( v = Graph[i].to ) ^ fa )
{
DFS( v, u );
f[u][0] += f[v][1] + 1;
f[u][1] += MIN( f[v][1], f[v][0] + 1 );
}
}
void clr()
{
cnt = 0;
for( int i = 1 ; i <= N ; i ++ )
vis[i] = head[i] = 0;
}
int main()
{
int T;
read( T );
while( T -- )
{
int ans1 = 0, ans2 = 0;
read( N ), read( M ), clr();
for( int i = 1, a, b ; i <= M ; i ++ )
read( a ), read( b ), a ++, b ++,
addEdge( a, b ), addEdge( b, a );
for( int i = 1 ; i <= N ; i ++ )
if( ! vis[i] )
{
DFS( i, 0 );
int tmp = MIN( f[i][0], f[i][1] );
ans1 += tmp / K;
ans2 += tmp % K;
}
write( ans1 ), putchar( ' ' ), write( M - ans2 ), putchar( ' ' ), write( ans2 ), putchar( '
' );
}
return 0;
}