题解 Silhouette

传送门

只会打暴力系列

首先应该是容斥,但行和列都需要考虑不知如何容
所以设法确定列,然后容斥行

先有一个结论:把 \(A, B\) 排序对答案不产生影响
所以我们先把 \(A, B\) 从大到小排序,然后从大到小枚举在 \(A, B\) 中出现过的数
可以发现每个这样的数确定了一个 \(L\) 形的范围的max
题解 Silhouette
分别容斥每个数对应的 \(L\) 形区域,令 \(f_i\) 为至少有 \(i\) 行不满足约束的方案数,\(s\) 为这个数
先考虑最左上角的 \(a*b\) 的矩形

\[f_i = \binom{a}{i}(s^i \times ((s+1)^{a-i}-s^{a-i}))^b \]

对着柿子还挺好理解的

  • 求一个数列/矩阵中最大值恰好为 \(s\) 的方案数: \(ans = s^{siz}-s^{siz-1}\)

然后推广到 \(L\) 形:
发现因为是从大到小,\(L\) 的两个臂已经被解除限制了,只考虑拐角处的矩形就好
那相应的柿子就是

\[f_i = \binom{c}{i}(s^i \times (s+1)^{c-i})^{b-d}(s^i \times ((s+1)^{a-i}-s^{a-i}))^d \]

对 \(f\) 进行容斥,最后把每个 \(s\) 对应的答案乘起来就好

Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
#define reg register int
//#define int long long 

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n;
int a[N], b[N];
const ll mod=1e9+7;

namespace force{
	int mp[10][10], t[N], ans;
	bool check() {
		memset(t, 0, sizeof(int)*(n+5));
		for (int j=1; j<=n; ++j)
			for (int i=1; i<=n; ++i)
				t[j]=max(t[j], mp[i][j]);
		for (int i=1; i<=n; ++i) if (t[i]!=a[i]) return 0;
		memset(t, 0, sizeof(int)*(n+5));
		for (int i=1; i<=n; ++i)
			for (int j=1; j<=n; ++j)
				t[i]=max(t[i], mp[i][j]);
		for (int i=1; i<=n; ++i) if (t[i]!=b[i]) return 0;
		return 1;
	}
	void dfs(int i, int j) {
		if (j>n) {j=1; ++i;}
		if (i>n) {if (check()) ++ans; return ;}
		int lim=min(b[i], a[j]);
		for (int k=0; k<=lim; ++k) {
			mp[i][j]=k;
			dfs(i, j+1);
		}
	}
	void solve() {
		dfs(1, 1);
		printf("%d\n", ans);
		exit(0);
	}
}

namespace task{
	ll fac[N], inv[N];
	ll qpow(ll a, int b) {ll ans=1ll; for (; b; a=a*a%mod, b>>=1) if (b&1) ans=ans*a%mod; return ans;}
	inline ll C(int n, int k) {return fac[n]*inv[k]%mod*inv[n-k]%mod;}
	inline int cmp(int a, int b) {return a>b;}
	void solve() {
		ll ans=1, tem=0;
		sort(a+1, a+n+1, cmp);
		sort(b+1, b+n+1, cmp);
		if (a[1]!=b[1]) {puts("0"); exit(0);}
		fac[0]=fac[1]=1; inv[0]=inv[1]=1;
		for (int i=2; i<=n; ++i) fac[i]=fac[i-1]*i%mod;
		for (int i=2; i<=n; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
		for (int i=2; i<=n; ++i) inv[i]=inv[i-1]*inv[i]%mod;
		for (int pos1=0,pos2=0,c=0,d=0,s=a[1]; pos1<=n||pos2<=n; c=0,d=0,tem=0) {
			while (1) {
				if (pos1<n && b[pos1+1]==s) ++pos1, ++c;
				else if (pos2<n && a[pos2+1]==s) ++pos2, ++d;
				else break;
			}
			for (int i=0,f=1; i<=c; ++i,f=-f) {
				//cout<<"i: "<<i<<endl;
				tem = ( tem + 1ll*f * C(c, i) * qpow(qpow(s, i)*qpow(s+1, c-i)%mod, pos2-d)%mod * qpow(qpow(s, i)*(((qpow(s+1, pos1-i)-qpow(s, pos1-i))%mod+mod)%mod)%mod, d)%mod )%mod;
			}
			//cout<<"L: "<<s<<' '<<pos1<<' '<<pos2<<' '<<c<<' '<<d<<' '<<tem<<endl;
			ans = ans*tem%mod;
			if (pos1==n && pos2==n) break;
			
			s=max(pos1<n?b[pos1+1]:0, pos2<n?a[pos2+1]:0);
		}
		printf("%lld\n", (ans%mod+mod)%mod);
		exit(0);
	}
}

signed main()
{
	n=read();
	for (int i=1; i<=n; ++i) a[i]=read();
	for (int i=1; i<=n; ++i) b[i]=read();
	//force::solve();
	task::solve();
	
	return 0;
}
上一篇:[Luogu P3811] 【模板】乘法逆元


下一篇:【csp模拟赛5】限制 (restrict.cpp)--数学