A Floor Tiles in a Park
本来是打算补模拟的,但是始终没有弄清那些重复那些不重复的部分,所以就去看了题解
然后看到了神仙解法
这位大佬是用拉格朗日插值法计算的,没看懂为什么答案一定是
k
−
1
k-1
k−1 次多项式,
我个人理解是我们最多在长选择
C
n
−
1
k
−
1
C_{n-1}^{k-1}
Cn−1k−1 条边,同理在宽选择
C
m
−
1
k
−
1
C_{m-1}^{k-1}
Cm−1k−1 条边,
之后是一堆分类讨论加去重,本质上是
k
−
1
k-1
k−1 次关于
n
n
n 的多项式与
k
−
1
k-1
k−1 次关于
m
m
m 的多项式的乘积,
所以答案是不超过
k
−
1
k-1
k−1 次多项式
(但是大佬代码一点注释没有,加上没有多项式基础,也没学过拉格朗日插值法,花了整整两天才看懂)
自学一元拉格朗日插值法后,看到了二元拉格朗日插值法的公式
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=1∑n[(k=1,k=i∏nxi−xkx−xk)j=1∑m(l=1,l=j∏myj−yly−yl)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=1∑kj=1∑k∏p=1,p=ik(i−p)li(n)∏q=1,q=jk(j−q)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=i∏k(n−j)
实测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;
}