题解 法阵

传送门

原题在这里
试图进行轮廓线DP,但是假了
正解的话建议康luogu题解
发现黑块和白块中至少有一个是两个梯形
而且这两个梯形的上底在同一条直线上
于是枚举这条线,再枚举其中一个点,另一个点的方案数前缀和优化求
发现一个点确定了之后与之相关的轮廓线就是从这个点走到角上的方案数,可以组合数求

  • 一些经过一些定点的凸多边形的方案数有时可以利用类似只能向上/向右走的走法数来求

一个比较神奇并且没有弄懂的事情是如果在枚举第二个点时组合数 \(\binom{n+m}{n}\) 写成 \(\binom{n+m+1}{n}\) 可以省掉对翻转情况的统计
这个待问

Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 4010
#define ll long long
#define fir first
#define sec second
#define make make_pair
#define pb push_back
//#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, m;
ll fac[N], inv[N];
const ll mod=998244353;
inline ll C(int n, int k) {return n<k?0:fac[n]*inv[k]%mod*inv[n-k]%mod;}

namespace force{
	int num[50][50], tot;
	pair<int, int> rk[N];
	vector<int> v1[N], v2[N];
	bool mp[50][50];
	bool check() {
		for (int i=1; i<=n; ++i) v1[i].clear();
		for (int i=1; i<=m; ++i) v2[i].clear();
		for (int i=1; i<=n; ++i) for (int j=1; j<=m; ++j) if (mp[i][j]) v1[i].pb(j), v2[j].pb(i);
		for (int i=1; i<=n; ++i) {
			if (v1[i].size()==0) return 0;
			
		}
	}
	
	void solve() {
		int lim=1<<(n*m);
		for (int i=1; i<=n; ++i) for (int j=1; j<=m; ++j) num[i][j]=++tot, rk[tot]=make(i, j);
		
	}
}

namespace task1{
	void solve() {
		if (n==1 || m==1) {puts("0"); exit(0);}
		ll tem=0, t1=0, t2=0, ans=0;
		for (int i=1; i<=m; ++i) tem=(tem+1ll*i*(C(m-i, 1)+C(m-i, 2))%mod)%mod;
		tem=tem*2%mod;
		for (int i=1; i<m; ++i) for (int j=i+1; j<m; ++j) t1=(t1+(j-i+1))%mod;
		for (int i=1; i<m; ++i) for (int j=i+1; j<m; ++j) for (int k=i; k<=j; ++k) t2=(t2+m-k)%mod;
		t1=t1*2%mod; t2=t2*2%mod;
		// cout<<(n-1)*tem<<endl;
		if (n>=1) ans=(ans+(n-1)*tem%mod)%mod;
		if (n>=2) ans=(ans+(n-2)*t1%mod)%mod;
		if (n>=3) ans=(ans+(n-3)*t2%mod)%mod;
		// ans=((n-1)*tem%mod + (n-2)*t1%mod + (n-3)*t2%mod)%mod;
		printf("%lld\n", ans);
		exit(0);
	}
}

namespace task2{
	ll f[110][110][100], g[110][110][110];
	void solve() {
		ll ans=0;
		if (n==1 || m==1) {puts("0"); exit(0);}
		for (int i=1; i<=m; ++i) for (int j=i; j<=m; ++j) f[1][i][j]=1; f[1][1][m]=0;
		for (int i=1; i<=m; ++i) for (int j=i; j<=m; ++j) g[n][i][j]=1; g[n][1][m]=0;
		for (int i=2; i<=n; ++i) {
			for (int j=1; j<=m; ++j) {
				for (int k=j; k<=m; ++k) if (!(j==1&&k==m)) {
					for (int s=1; s<=j; ++s) {
						for (int t=k; t<=m; ++t) {
							f[i][j][k]=(f[i][j][k]+f[i-1][s][t])%mod;
						}
					}
				}
			}
		}
		for (int i=n-1; i; --i) {
			for (int j=1; j<=m; ++j) {
				for (int k=j; k<=m; ++k) if (!(j==1&&k==m)) {
					for (int s=1; s<=j; ++s) {
						for (int t=k; t<=m; ++t) {
							g[i][j][k]=(g[i][j][k]+g[i+1][s][t])%mod;
						}
					}
				}
			}
		}
		for (int i=1; i<n; ++i) {
			for (int j=1; j<=m; ++j) {
				for (int k=j; k<=m; ++k) if (!(j==1&&k==m)) {
					for (int s=1; s<=m; ++s) {
						for (int t=s; t<=m; ++t) if (!(s==1&&t==m) && (t<j||s>k)) {
							ans=(ans+f[i][j][k]*g[i+1][s][t])%mod;
						}
					}
				}
			}
		}
		printf("%lld\n", ans);
		exit(0);
	}
}

namespace task{
	ll ans1, ans2;
	void solve() {
		for (int i=1; i<m; ++i) {
			ll sum1=0, sum2=0, lst=0;
			for (int j=1; j<n; ++j) {
				lst=C((m-i)+(j-1), m-i)*C((m-i-1)+(n-j+1), (m-i-1))%mod;
				sum1=(sum1+lst)%mod;
				ans1=(ans1+C(i+(n-j-1), i)*C((i-1)+j, i-1)%mod*sum1)%mod;
			}
		}
		// cout<<"ans: "<<ans1<<' '<<ans2<<endl;
		printf("%lld\n", 2*ans1%mod);
		exit(0);
	}
}

signed main()
{
	freopen("magic.in", "r", stdin);
	freopen("magic.out", "w", stdout);

	n=read(); m=read();
	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;
	// task1::solve();
	// task2::solve();
	task::solve();

	return 0;
}
posted @ 2021-11-16 21:40  Administrator-09  阅读(0)  评论(0编辑  收藏  举报