题目链接

题目大意:给定一个 \(n\) 列的表格,每一列有 \(a_i\) 行,底部对齐。往里面填入 \(k\) 个数字,要求每一列最多只有一个数字,每一行一段内最多只有一个数字(中间断开了不算),求方案数。

笛卡尔树,dp,计数


分析:

最大子矩形可以用笛卡尔树来做,此题每一段中间断开的特性,和小根笛卡尔树构造方法也十分相似(每次我们找到最低的一列分开原表格)。

我们将原表格建出一个小根笛卡尔树,那么笛卡尔树的每一个节点都代表了一个子矩形(具体可以看一下其他题解,这里就不重复放图了)

我们设 \(w(u,a,b)\),表示在 \(u\) 这个节点所在子矩形内部,放 \(a\) 个数,其中 \(u\) 的子树选了 \(b\) 个的方案数。

那么这个子矩形的宽 \(n=v[u]-v[faz[u]]\),这个子矩形的长(是实际可以选的列数) \(m=siz[u]-b\)

其中选 \(k\) 个行列互不相同的数,方案数为 \(\binom{n}{k}\binom{m}{k}k!\)(相当于行列中任选 \(k\) 个出来,再任意组合)

然后直接两个子树的方案数卷一下,再和 \(w\) 卷一下就可以了

注意合并的时候要用 \(siz\) 优化,确保每一对节点只会在 \(lca\) 处被合并(貌似和 \(w\) 卷的时候并不能优化)

#include <cstdio>
#include <cctype>
#include <cstring>
#pragma GCC optmize(2)
using namespace std;
typedef long long ll;
constexpr int maxn = 512,inf = 0x7fffffff,mod = 1e9 + 7;
struct IO{//-std=c++11,with cstdio and cctype
	private:
		static constexpr int ibufsiz = 1 << 20;
		char ibuf[ibufsiz + 1],*inow = ibuf,*ied = ibuf;
		static constexpr int obufsiz = 1 << 20;
		char obuf[obufsiz + 1],*onow = obuf;
		const char *oed = obuf + obufsiz;
	public:
		inline char getchar(){
			#ifndef ONLINE_JUDGE
				return ::getchar();
			#else
				if(inow == ied){
					ied = ibuf + sizeof(char) * fread(ibuf,sizeof(char),ibufsiz,stdin);
					*ied = '\0';
					inow = ibuf;
				}
				return *inow++;
			#endif
		}
		template<typename T>
		inline void read(T &x){
			static bool flg;flg = 0;
			x = 0;char c = getchar();
			while(!isdigit(c))flg = c == '-' ? 1 : flg,c = getchar();
			while(isdigit(c))x = x * 10 + c - '0',c = getchar();
			if(flg)x = -x;
		}
		template <typename T,typename ...Y>
		inline void read(T &x,Y&... X){read(x);read(X...);}
		inline int readi(){static int res;read(res);return res;}
		inline long long readll(){static long long res;read(res);return res;}
		
		inline void flush(){
			fwrite(obuf,sizeof(char),onow - obuf,stdout);
			fflush(stdout);
			onow = obuf;
		}
		inline void putchar(char c){
			#ifndef ONLINE_JUDGE
				::putchar(c);
			#else
				*onow++ = c;
				if(onow == oed){
					fwrite(obuf,sizeof(char),obufsiz,stdout);
					onow = obuf;
				}
			#endif
		}
		template <typename T>
		inline void write(T x,char split = '\0'){
			static unsigned char buf[64];
			if(x < 0)putchar('-'),x = -x;
			int p = 0;
			do{
				buf[++p] = x % 10;
				x /= 10;
			}while(x);
			for(int i = p;i >= 1;i--)putchar(buf[i] + '0');
			if(split != '\0')putchar(split);
		}
		inline void lf(){putchar('\n');}
		~IO(){
			fwrite(obuf,sizeof(char),onow - obuf,stdout);
		}
}io;
template <typename A,typename B>
inline void chkmin(A &x,const B &y){if(y < x)x = y;}
template <typename A,typename B>
inline void chkmax(A &x,const B &y){if(y > x)x = y;}

inline int add(const int a,const int b){return (a + b) % mod;}
inline int mul(const int a,const int b){return (1ll * a * b) % mod;}
inline int qpow(int base,int b){
	int res = 1;
	while(b){
		if(b & 1)res = mul(res,base);
		base = mul(base,base);
		b >>= 1;
	}
	return res;
}
inline int inv(const int x){return qpow(x,mod - 2);}
inline int calc(const int a,const int b){return mul(a,inv(b));}

int fac[int(1e6 + 100)];
inline int C(const int n,const int m){
	return m > n ? 0 : mul(fac[n],mul(inv(fac[m]),inv(fac[n - m])));
}
inline void init(){
	fac[0] = 1;
	for(int i = 1;i < (1e6 + 100);i++)fac[i] = mul(fac[i - 1],i);
}

int tmp[maxn],faz[maxn],val[maxn],stk[maxn],siz[maxn],ls[maxn],rs[maxn],f[maxn][maxn],top,k,n;
inline int w(const int u,const int a,const int b){
	const int n = val[u] - val[faz[u]],m = siz[u] - b;
	return mul(mul(C(n,a),C(m,a)),fac[a]);
}
inline void dp(int u){
	siz[u] = 1;
	if(ls[u])faz[ls[u]] = u,dp(ls[u]);
	if(rs[u])faz[rs[u]] = u,dp(rs[u]);
	siz[u] += siz[ls[u]];
	siz[u] += siz[rs[u]];
	memset(tmp,0,sizeof(tmp));
	for(int i = 0;i <= siz[ls[u]];i++)
		for(int j = 0;j <= siz[rs[u]];j++)
			if(i + j <= k)tmp[i + j] = add(tmp[i + j],mul(f[ls[u]][i],f[rs[u]][j]));
	tmp[0] = 1;
	for(int i = 0;i <= siz[u];i++)
		for(int j = 0;i + j <= siz[u];j++)
			if(i + j <= k)f[u][i + j] = add(f[u][i + j],mul(tmp[i],w(u,j,i)));
}
int main(){
#ifndef ONLINE_JUDGE
	freopen("fafa.in","r",stdin);
#endif
	init();
	io.read(n,k);
	for(int i = 1;i <= n;i++)io.read(val[i]);
	for(int i = 1;i <= n;i++){
		int k = top;
		while(k && val[i] <= val[stk[k]])k--;
		if(k)rs[stk[k]] = i;
		if(k < top)ls[i] = stk[k + 1];
		stk[++k] = i;
		top = k;
	}
	int rt = stk[1];
	f[0][0] = 1;
	dp(rt);	
	io.write(f[rt][k],'\n');
	return 0;
}

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2021-12-24
  • 2021-07-19
  • 2021-09-05
  • 2021-08-09
  • 2022-12-23
  • 2021-06-04
猜你喜欢
  • 2021-08-06
  • 2022-12-23
  • 2021-06-01
  • 2021-05-22
  • 2021-06-27
  • 2022-01-17
  • 2022-12-23
相关资源
相似解决方案