Live2D

Solution -「多校联训」消失的运算符

Description

  Link.

  给定长度为 n 的合法表达式序列 s,其中数字仅有一位正数,运算符仅有 - 作为占位。求将其中恰好 k- 替换为 +,其余 - 替换为 * 的所有方案得到的表达式结果之和。答案模 (109+7)

  n105(可能有无意义的多层括号嵌套),- 的总数 m2.5×103

Solution

  复杂表达式问题,应当考虑按匹配括号建树,并在树上 DP。

  考虑树上结点 u 以及它的孩子们 v1,,vc,它们代表了形如 u=(v1)-(v2)-...-(vc) 的表达式。令 fu,i 表示 u 从左到右合并了一些孩子,用了 i+ 时,所有表达式结果之和;gi 表示 u 从左到右合并了一些孩子,用了 i+ 时,所有后缀乘积之和。设已合并的孩子们共有 su+,现欲合并孩子 v,其有 sv+,转移分 +, * 讨论:

fu,i+j+1+(svj)fu,i+(sui)fv,j,fu,i+j+(svj)(fu,igi)+gifv,i,gi+j+1+(sui)fv,j,gi+j+gifv,j,su+sv+1.

  转移复杂度是树上背包,总复杂度 O(n+m2)

Code

/*~Rainybunny~*/

#pragma GCC optimize( "Ofast" )

#include <bits/stdc++.h>

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

const int MAXN = 2e5, MAXM = 5e3, MOD = 1e9 + 7;
int n, k, m, node, siz[MAXM + 5], mtc[MAXN * 3 + 5];
int comb[MAXM + 5][MAXM + 5], f[MAXM + 5][MAXM + 5];
char str[MAXN * 3 + 5], tmps[MAXN + 5];

inline int imin( const int u, const int v ) { return u < v ? u : v; }
inline int imax( const int u, const int v ) { return u < v ? v : u; }
inline int mul( const int u, const int v ) { return 1ll * u * v % MOD; }
inline int sub( int u, const int v ) { return ( u -= v ) < 0 ? u + MOD : u; }
inline int add( int u, const int v ) { return ( u += v ) < MOD ? u : u - MOD; }
inline void addeq( int& u, const int v ) { ( u += v ) >= MOD && ( u -= MOD ); }

inline void init() {
	int len = n; n = 0;
	rep ( i, 1, len ) {
		if ( '0' <= tmps[i] && tmps[i] <= '9' ) {
			str[++n] = '(', str[++n] = tmps[i], str[++n] = ')';
		} else {
			str[++n] = tmps[i];
			m += tmps[i] == '-';
		}
	}
	
	static int stk[MAXN * 3 + 5]; int top = 0;
	rep ( i, 1, n ) {
		if ( str[i] == '(' ) stk[++top] = i;
		else if ( str[i] == ')' ) mtc[mtc[stk[top]] = i] = stk[top], --top;
	}
	
	comb[0][0] = 1;
	rep ( i, 1, m ) {
		comb[i][0] = 1;
		rep ( j, 1, i ) comb[i][j] = add( comb[i - 1][j - 1], comb[i - 1][j] );
	}
}

inline int solve( int l, int r ) {
	if ( mtc[l] == r ) return solve( l + 1, r - 1 );
	int u = ++node, *fu = f[u];
	if ( l == r ) return fu[0] = str[l] ^ '0', u;
	
	int g[MAXM + 5];
	for ( int p = l, v, fir = true; p < r; ) {
		if ( str[p] != '(' ) { ++p; continue; }
		const int *fv = f[v = solve( p, mtc[p] )]; p = mtc[p] + 1;
		
		if ( fir ) {
			rep ( i, 0, siz[v] ) fu[i] = g[i] = fv[i];
			siz[u] = siz[v], fir = false;
			continue;
		}
		
		static int tmp[MAXM + 5];
		rep ( i, 0, siz[u] ) rep ( j, 0, siz[v] ) {
			addeq( tmp[i + j + 1], add( mul( fu[i], comb[siz[v]][j] ),
			  mul( fv[j], comb[siz[u]][i] ) ) );
			addeq( tmp[i + j], add( mul( sub( fu[i], g[i] ),
			  comb[siz[v]][j] ), mul( g[i], fv[j] ) ) );
		}
		rep ( i, 0, siz[u] + siz[v] + 1 ) fu[i] = tmp[i], tmp[i] = 0;
		rep ( i, 0, siz[u] ) rep ( j, 0, siz[v] ) {
			addeq( tmp[i + j], mul( g[i], fv[j] ) );
			addeq( tmp[i + j + 1], mul( comb[siz[u]][i], fv[j] ) );
		}
		rep ( i, 0, siz[u] + siz[v] + 1 ) g[i] = tmp[i], tmp[i] = 0;
		siz[u] += siz[v] + 1;
	}
	return u;
}

int main() {
	freopen( "operator.in", "r", stdin );
	freopen( "operator.out", "w", stdout );
	
	scanf( "%d %d %s", &n, &k, tmps + 1 );
	init(); //, fprintf( stderr, "%s\n", str + 1 );
	printf( "%d\n", f[solve( 1, n )][k] );
	return 0;
}
posted @   Rainybunny  阅读(166)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2020-10-04 Solution -「LOCAL」「cov. 牛客多校 2020 第三场 I」礼物
点击右上角即可分享
微信分享提示