1. 程式人生 > 其它 >[提高組集訓2021] 消失的運算子

[提高組集訓2021] 消失的運算子

一、題目

給定一個長度為 \(n\) 的表示式,表示式只出現括號、減號和數字 \(1\sim 9\),設一共有 \(m\) 個減號。

求出把 \(m\) 個減號其中 \(k\) 個替換成加號,\(m-k\) 個替換個乘號的所有表示式之和,答案模 \(1e9+7\)

\(n\leq 10^5,m\leq 2500\)

二、解法

首先考慮沒有括號怎麼做,注意這裡不能直接設 \(f[i][j]\) 表示前 \(i\) 個數字選 \(j\) 個加號的值,因為如果下一個符號是乘號的話你是轉移不動的。那麼我們特殊處理這種情況,設 \(f[i][j]\) 表示前 \(i\) 個數字選 \(j\) 個加號,不計入最後一段連乘的值,\(g[i][j]\)

表示計入最後一段連乘的值,\(num[i][j]\) 表示方案數。

因為我們的 \(dp\) 記錄的是所有情況的和,利用分配律就可以簡單轉移了。

如果有括號怎麼辦呢?我們用類似分治的方法建出一棵括號樹,把每一層最淺的那些括號段取出來遞迴下去,合併的時候當成沒有括號的情況按順序合併,複雜度就是樹揹包的 \(O(m^2)\)

當然本題的難點是實現,我們如何找到最淺的括號段呢?對於每個減號記錄左邊未匹配的左括號個數 \(match[i]\),那麼每一層都是 \(match\) 最小的減號作為分隔符號,以其為端點分治即可。

//I wish that I was bulletproof
#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
const int M = 2505;
const int N = 5005;
const int MOD = 1e9+7;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,f[N][M],g[N][M],num[N][M],len[N];
int cnt,l1,l2,val[M],mt[M],tf[M],tg[M],tn[M];
char s[1000005];
void div(int x,int l,int r)
{
	if(l==r)
	{
		f[x][0]=val[l];
		num[x][0]=1;
		return ;
	}
	int mi=MOD;vector<int> v;
	for(int i=l;i<r;i++)
		mi=min(mi,mt[i]);
	for(int i=l;i<r;i++)
		if(mt[i]==mi) v.push_back(i);
	int y=++cnt;
	div(y,l,v[0]);
	len[x]=len[y];
	for(int i=0;i<=len[x];i++)
	{
		f[x][i]=0;g[x][i]=f[y][i];
		num[x][i]=num[y][i];
	}
	for(int i=0;i<v.size();i++)
	{
		y=++cnt;
		div(y,v[i]+1,(i+1==v.size())?r:v[i+1]);
		for(int j=0;j<=len[x];j++)
		{
			tf[j]=f[x][j];tg[j]=g[x][j];tn[j]=num[x][j];
			f[x][j]=g[x][j]=num[x][j]=0;
		}
		for(int j=0;j<=len[x];j++)
			for(int k=0;k<=len[y];k++)
			{
				//*
				int t=j+k;
				f[x][t]=(f[x][t]+tf[j]*num[y][k])%MOD;
				g[x][t]=(g[x][t]+tg[j]*f[y][k])%MOD;
				num[x][t]=(num[x][t]+tn[j]*num[y][k])%MOD;
				//+
				t=j+k+1;
				f[x][t]=(f[x][t]+(tf[j]+tg[j])*num[y][k])%MOD;
				g[x][t]=(g[x][t]+tn[j]*f[y][k])%MOD;
				num[x][t]=(num[x][t]+tn[j]*num[y][k])%MOD;
			}
		len[x]+=len[y]+1;
	}
	for(int j=0;j<=len[x];j++)
		f[x][j]=(f[x][j]+g[x][j])%MOD;
}
signed main()
{
	freopen("operator.in","r",stdin);
	freopen("operator.out","w",stdout);
	n=read();m=read();scanf("%s",s+1);
	for(int i=1,zz=0;i<=n;i++)
	{
		if(s[i]=='(') zz++;
		else if(s[i]==')') zz--;
		else if('0'<=s[i] && s[i]<='9')
			val[++l1]=s[i]-'0';
		else mt[++l2]=zz;
	}
	div(cnt=1,1,l1);
	printf("%lld\n",f[1][m]);
}