Live2D

Solution -「AGC 026D」Histogram Coloring

\(\mathcal{Description}\)

  Link.

  有 \(n\) 列下底对齐的方格纸排成一行,第 \(i\) 列有 \(h_i\) 个方格。将每个方格染成黑色或白色,求使得任意完整 \(2\times2\) 矩形内恰有两个白色(和两个黑色)的方案数。答案模 \(10^9+7\)

  \(n\le100\)\(h_i\le10^9\)

\(\mathcal{Solution}\)

  小清新 DP 题叭。

  首先考虑在完整的网格图里染色,若某一行完成染色,那么下一行的方案并不多:

  • 若当前行存在相邻同色格,则下一行必须为当前行颜色取反;
  • 否则即当前行为黑白或白黑交替,则下一行可以与当前行全部相同或全部相反。

  把这一结论放在本题,建出小根笛卡尔树(根据题意,当前区间最小值应缩为一点,严格意义上不是笛卡尔树,不难意会 qwq),DP。转移问题形如将若干小块网格下面垫一大块完整网格图,求方案数。所以,令 \(f(u,0/1)\) 表示完成 \(u\) 所代表区间的染色,且最下一层是否是黑白交替状的方案数。转移平凡,可参考易读的代码。

  转移里有个快速幂,最终复杂度为 \(\mathcal O(n\log h)\)

\(\mathcal{Code}\)

/* Clearink */

#include <cstdio>

#define rep( i, l, r ) for ( int i = l, repEnd##i = r; i <= repEnd##i; ++i )
#define per( i, r, l ) for ( int i = r, repEnd##i = l; i >= repEnd##i; --i )

const int MAXN = 100, MOD = 1e9 + 7;
int n, h[MAXN + 5];

int node, ecnt, lef[MAXN + 5], rig[MAXN + 5],
	hgt[MAXN + 5], tot[MAXN + 5], head[MAXN + 5];
struct Edge { int to, nxt; } graph[MAXN + 5];

inline int mul( const long long a, const int b ) { return a * b % MOD; }
inline int sub( int a, const int b ) { return ( a -= b ) < 0 ? a + MOD : a; }
inline void subeq( int& a, const int b ) { ( a -= b ) < 0 && ( a += MOD ); }
inline int add( int a, const int b ) { return ( a += b ) < MOD ? a : a - MOD; }
inline void addeq( int& a, const int b ) { ( a += b ) >= MOD && ( a -= MOD ); }
inline int mpow( int a, int b ) {
	int ret = 1;
	for ( ; b; a = mul( a, a ), b >>= 1 ) ret = mul( ret, b & 1 ? a : 1 );
	return ret;
}

inline void link( const int s, const int t ) {
	graph[++ecnt] = { t, head[s] }, head[s] = ecnt;
}

inline int build( const int l, const int r ) {
	if ( l > r ) return 0;
	
	int u = ++node;
	hgt[u] = 0x3f3f3f3f, tot[u] = 0;
	lef[u] = l, rig[u] = r;
	rep ( i, l, r ) {
		if ( h[i] < hgt[u] ) hgt[u] = h[i], tot[u] = 0;
		tot[u] += h[i] == hgt[u];
	}

	for ( int i = l, j; i <= r; i = j + 1 ) {
		if ( h[j = i] == hgt[u] ) continue;
		while ( j + 1 <= r && h[j + 1] != hgt[u] ) ++j;
		int v = build( i, j );
		link( u, v ), hgt[v] -= hgt[u];
	}

	return u;
}

int f[MAXN + 5][2];

inline void solve( const int u ) {
	int &f0 = f[u][0] = 1, &f1 = f[u][1] = 2, len = rig[u] - lef[u] + 1;
	for ( int i = head[u], v; i; i = graph[i].nxt ) {
		solve( v = graph[i].to ), len -= rig[v] - lef[v] + 1;
		f0 = mul( f0, add( f[v][0], add( f[v][1], f[v][1] ) ) ),
		f1 = mul( f1, f[v][1] );
	}
	f0 = mul( f0, mpow( 2, len ) ),
	subeq( f0, f1 ),
	f1 = mul( f1, mpow( 2, hgt[u] - 1 ) );
}

int main() {
	scanf( "%d", &n );
	rep ( i, 1, n ) scanf( "%d", &h[i] );

	solve( build( 1, n ) );
	printf( "%d\n", add( f[1][0], f[1][1] ) );
	return 0;
}

posted @ 2021-05-03 20:10  Rainybunny  阅读(121)  评论(0编辑  收藏  举报