2022牛客多校7 A Floor Tiles in a Park

博客介绍了使用拉格朗日插值法解决一个关于矩形划分的问题,具体涉及多項式乘积、分类讨论和去重。作者详细解析了二元拉格朗日插值法的公式,并给出了一段C++代码实现,该代码通过预处理多项式和矩阵运算求解问题。最后,博主分享了暴力模拟的复杂度和实际运行时间,并提供了AC代码。
摘要由CSDN通过智能技术生成

A Floor Tiles in a Park

本来是打算补模拟的,但是始终没有弄清那些重复那些不重复的部分,所以就去看了题解
然后看到了神仙解法
这位大佬是用拉格朗日插值法计算的,没看懂为什么答案一定是 k − 1 k-1 k1 次多项式,
我个人理解是我们最多在长选择 C n − 1 k − 1 C_{n-1}^{k-1} Cn1k1 条边,同理在宽选择 C m − 1 k − 1 C_{m-1}^{k-1} Cm1k1 条边,
之后是一堆分类讨论加去重,本质上是 k − 1 k-1 k1 次关于 n n n 的多项式与 k − 1 k-1 k1 次关于 m m m 的多项式的乘积,
所以答案是不超过 k − 1 k-1 k1 次多项式
(但是大佬代码一点注释没有,加上没有多项式基础,也没学过拉格朗日插值法,花了整整两天才看懂)
自学一元拉格朗日插值法后,看到了二元拉格朗日插值法的公式
f ( x , y ) = ∑ i = 1 n [ ( ∏ k = 1 , k ≠ i n x − x k x i − x k ) ∑ j = 1 m ( ∏ l = 1 , l ≠ j m y − y l y j − y l ) f ( x i , y j ) ] f(x,y) = \sum_{i=1}^n\bigg[ ( \prod_{k=1,k≠i}^n\frac{x-x_k}{x_i-x_k})\sum_{j=1}^m(\prod_{l=1,l≠j}^m\frac{y-y_l}{y_j-y_l})f(x_i,y_j)\bigg] f(x,y)=i=1n[(k=1,k=inxixkxxk)j=1m(l=1,l=jmyjylyyl)f(xi,yj)]
在该题中 n n n m m m 等价,如果枚举 1 1 1 n n n 1 1 1 m m m 每个长宽为 i , j i,j i,j 的矩形分成 k k k 块的方案数 f k ( i , j ) f_k(i,j) fk(i,j)为插值代入的点
则原式可以化简为:
f k ( n , m ) = ∑ i = 1 k ∑ j = 1 k l i ( n ) ∏ p = 1 , p ≠ i k ( i − p ) l j ( m ) ∏ q = 1 , q ≠ j k ( j − q ) f k ( i , j ) f_k(n,m) = \sum_{i=1}^k\sum_{j=1}^k \frac{l_i(n)}{\prod_{p=1,p≠i}^k(i-p)}\frac{l_j(m)}{\prod_{q=1,q≠j}^k(j-q)}f_k(i,j) fk(n,m)=i=1kj=1kp=1,p=ik(ip)li(n)q=1,q=jk(jq)lj(m)fk(i,j)
其中 l i ( n ) l_i(n) li(n) 是一个关于 n n n 多项式,为
l i ( n ) = ∏ j = 1 , j ≠ i k ( n − j ) l_i(n) = \prod_{j=1,j≠i}^k(n-j) li(n)=j=1,j=ik(nj)
实测386s跑出 k = 5 k=5 k=5 时的答案
总比赛时写不出来好太多了

  (time_end - time_start)
= (386223)

那么上代码

#include<bits/stdc++.h>
using namespace std;
namespace ACM
{
#define ll long long
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
	template<typename T>
	void dbg_out(T x) {cerr<<x<<")"<<endl;}
	void dbg_out() {cerr<<")"<<endl;}
	template<typename Head,typename ...Tail>
	void dbg_out(Head H,Tail... T) {cerr<<H<<", ";dbg_out(T...);}
#define debug(...) cerr<<"  ("<<#__VA_ARGS__<<")\n= (",dbg_out(__VA_ARGS__)
	template <class T> using vc = vector<T>;
	template <class T> using vvc = vector<vc<T> >;
	template<typename T, typename T2>ll ksm(ll x, T n, T2 mod) { ll ret = 1; if (x == 1) return 1; while (n) { if(n & 1) ret = ret * x % mod; n >>= 1; x = x * x % mod; } return ret % mod; }
	int T_T = 1;
}
using namespace ACM;

void init();
void solve();
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr); cout.tie(nullptr);
	init();
	while (T_T--)solve();
	return 0;
}

// const int dx[] = {0, 0, -1, 1}, dy[] = {-1, 1, 0, 0};
const int inf = 0x3f3f3f3f;
const ll  INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
const int N = 1e5 + 5;

void init()
{
	// cin >> T_T;
}

namespace ploysum
{
	const int N = 7;
	ll a[N];
	ll l[N];    //用来存储多项式(x-x1)*(x-x2)...*(x-xn)
	ll bf[N];    //备份fi多项式

	inline void mul(ll f[], int len, ll t)    //len-1次多项式f乘上(x + t)
	{
		ll last = 0, now;
		rep(i, 0, len)
		{
			now = f[i];
			f[i] = (f[i] * t + last) % mod;
			last = now;
		}
	}

	inline void dev(ll f[], ll r[], ll t, int len) //len次多项式f除以(x + t),保存在r中
	{
		ll last = f[len] , now;
		per(i, len - 1, 0)
		{
			now = (f[i] - t * last) % mod;
			r[i] = last;
			last = now;
		}
	}


	bool st[N][N];
	ll cnt = 0;

	inline bool check(int x1, int x2, int y1, int y2)
	{
		rep(i, x1, x2) rep(j, y1, y2) if (st[i][j]) return false;
		return true;
	}

	inline void change(int x1, int x2, int y1, int y2, bool si)
	{
		if (si) cnt += (x2 - x1 + 1) * (y2 - y1 + 1);
		else cnt -= (x2 - x1 + 1) * (y2 - y1 + 1);
		rep(i, x1, x2) rep(j, y1, y2) st[i][j] = si;
	}

	
	inline ll getans(int n, int m, int k)//暴力模拟程序
	{
		if (!k) return (cnt == n * m ? 1 : 0);
		ll ret = 0;
		rep(x1, 1, n) rep(x2, x1, n) rep(y1, 1, m) rep(y2, y1, m)
		{
			if (check(x1, x2, y1, y2))
			{
				change(x1, x2, y1, y2, true);
				ret += getans(n, m, k - 1);
				change(x1, x2, y1, y2, false);
			}
		}
		return ret;
	}

	ll lx[N][N];//第一维为 预处理多项式/(x-i) 第二维为x的指数j(x^j)
	ll xs[N][N];//系数矩阵(x^i * y^j)
	ll ans[N][N];//答案数组 需要暴力得出

	void out(int n)
	{
		cout << "    {" << endl;
		rep(i, 0, n)
		{
			cout << "        {" << xs[i][0];
			rep(j, 1, n) cout << ',' << xs[i][j];
			cout << '}';
			if (i != n)cout << ',';
			cout << endl;
		}
		cout << "    }" << endl;
	}

	void init(int k)
	{
		//预处理(x-1)*(x-2)...*(x-k)
		l[0] = 1;
		rep(i, 1, k) mul(l, i, -i);	//当前多项式乘上(x - i)

		rep(i, 1, k)
		{
			ll wi = 1;//重心权
			rep(j, 1, k) if (j != i) wi = wi * (i - j) % mod;

			//wi -> 1/wi
			wi = ksm(wi, mod - 2, mod);

			dev(l, lx[i], -i, k);//预处理的多项式/(x-i)

			rep(j, 0, k - 1) lx[i][j] = wi * lx[i][j] % mod;
		}
			
		rep(i, 1, k) rep(j, 1, k) //枚举 /(x-i) 与 /(x-j)
		{
			vvc<ll> tmp(N, vc<ll>(N, 0));

			rep(p, 0, k - 1) rep(q, 0, k - 1)	//枚举x^p y^q
				tmp[p][q] = lx[i][p] * lx[j][q] % mod;

			ll si; //当前矩阵的系数
			if (i > j) si = ans[j][i]; //因为对称性
			else si = getans(i, j, k);
			ans[i][j] = si;
			rep(t, 2, k) si /= t;
			debug(i, j, si);

			rep(p, 0, k - 1) rep(q, 0, k - 1)
				xs[p][q] = (xs[p][q] + si * tmp[p][q]) % mod;
		}

		out(5);
	}
}

int n, m, k;

void solve()
{
	cin >> k;
	clock_t time_start = clock();
	ploysum::init(k);
	clock_t time_end = clock();
    debug(time_end - time_start);
}

然后是AC代码

#include<bits/stdc++.h>
using namespace std;
namespace ACM
{
#define ll long long
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
	template<typename T>
	void dbg_out(T x) {cerr<<x<<")"<<endl;}
	void dbg_out() {cerr<<")"<<endl;}
	template<typename Head,typename ...Tail>
	void dbg_out(Head H,Tail... T) {cerr<<H<<", ";dbg_out(T...);}
#define debug(...) cerr<<"  ("<<#__VA_ARGS__<<")\n= (",dbg_out(__VA_ARGS__)
	template <class T> using vc = vector<T>;
	template <class T> using vvc = vector<vc<T> >;
	template<typename T, typename T2>ll ksm(ll x, T n, T2 mod) { ll ret = 1; if (x == 1) return 1; while (n) { if(n & 1) ret = ret * x % mod; n >>= 1; x = x * x % mod; } return ret % mod; }
	int T_T = 1;
}
using namespace ACM;

void init();
void solve();
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr); cout.tie(nullptr);
	init();
	while (T_T--)solve();
	return 0;
}

// const int dx[] = {0, 0, -1, 1}, dy[] = {-1, 1, 0, 0};
const int inf = 0x3f3f3f3f;
const ll  INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
const int N = 7;

void init()
{
	// cin >> T_T;
}

ll xs[6][6][6] = {
	{
		{0,0,0,0,0,0},
		{0,0,0,0,0,0},
		{0,0,0,0,0,0},
		{0,0,0,0,0,0},
		{0,0,0,0,0,0},
		{0,0,0,0,0,0}
	},
	{
		{1,0,0,0,0,0},
		{0,0,0,0,0,0},
		{0,0,0,0,0,0},
		{0,0,0,0,0,0},
		{0,0,0,0,0,0},
		{0,0,0,0,0,0}
	},
	{
		{-2,1,0,0,0,0},
		{1,0,0,0,0,0},
		{0,0,0,0,0,0},
		{0,0,0,0,0,0},
		{0,0,0,0,0,0},
		{0,0,0,0,0,0}
	},
	{
		{6,-499122182,499122177,0,0,0},
		{-499122182,4,0,0,0,0},
		{499122177,0,0,0,0,0},
		{0,0,0,0,0,0},
		{0,0,0,0,0,0},
		{0,0,0,0,0,0}
	},
	{
		{-23,-665496207,-499122183,-831870294,0,0},
		{-665496207,998244321,-499122171,0,0,0},
		{-499122183,-499122171,0,0,0,0},
		{-831870294,0,0,0,0,0},
		{0,0,0,0,0,0},
		{0,0,0,0,0,0}
	},
	{
		{104,-748683419,707089806,-249561093,291154603,0},
		{-748683419,332748336,-499122247,332748122,0,0},
		{707089806,-499122247,16,0,0,0},
		{-249561093,332748122,0,0,0,0},
		{291154603,0,0,0,0,0},
		{0,0,0,0,0,0}
	}

};

ll n, m, k;

ll calc(ll n, ll m, ll k)
{
	ll ans = 0;
	for (ll i = 0, x = 1; i < k; i++, x = x * n % mod)
		for (ll j = 0, y = 1; j < k; j++, y = y * m % mod)
			ans = (ans + x * y % mod * xs[k][i][j]) % mod;
	return (ans + mod) % mod;
}

void solve()
{
	cin >> n >> m >> k;
	cout << calc(n, m, k) << endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值