abc327题解
F - Apples
题意:给定二维平面上的N个点坐标和一个大小为D*W的矩形框,问这个框最多可以框住多少个点。
解答:考虑一个点(X,Y)能够被框住当且仅当框的左下角处于 [ X − D , X ] × [ Y − W , Y ] [X-D,X]\times[Y-W,Y] [X−D,X]×[Y−W,Y]区域内。在y轴上建立线段树,按照x坐标从小到大顺序依次加入点(X,Y),即[Y-W,Y]区间加1,同时将x坐标已经离开区域的点(X,Y)删去,即[Y-W,Y]区间减1,这个操作相当于从左往右移动框,每次移动后查询区间最大值,更新答案。
G - Many Good Tuple Problems
题意:N个点的图,要赋予它M条边(边带标号,同时每条边区分起点和终点),要求加完边后不存在奇圈,问有多少种加边方案。
解答:即数有多少个N个点M条边(均带标号,可有重边)的二分图(记为A(N,M))然后乘以 2 M 2^M 2M(因为每条边区分起终点)即可。 A ( N , M ) = ∑ k = 1 m a x E d g e f ( N , k ) × b ( M , k ) A(N,M)=\sum_{k=1}^{maxEdge}f(N,k)\times b(M,k) A(N,M)=∑k=1maxEdgef(N,k)×b(M,k), m a x E d g e = n 2 / 4 maxEdge=n^2/4 maxEdge=n2/4,f(N,k)为N个带标号点和k个无标号边简单二分图个数,b(M,k)为将M个带标号边分成k个不同集合的方案数,即为 k ! { M k } k!{M\brace k} k!{kM},第二类斯特林数 { M k } {M\brace k} {kM}公式为 S k = 1 k ! ∑ i = 0 k ( − 1 ) k − i ( k i ) i M S_k=\frac{1}{k!}\sum_{i=0}^{k}(-1)^{k-i}\binom{k}{i}i^M Sk=k!1∑i=0k(−1)k−i(ik)iM(可通过二项式反演得到)。
记g(N,k)是N个点k个边的染色二分图个数,即 g ( N , k ) = ∑ i = 1 N − 1 ( N i ) ( i ∗ ( N − i ) k ) g(N,k)=\sum_{i=1}^{N-1}\binom{N}{i}\binom{i*(N-i)}{k} g(N,k)=∑i=1N−1(iN)(ki∗(N−i)),而连通染色二分图个数 h ( N , k ) = g ( N , k ) − ∑ i = 0 N − 1 ∑ j = 0 k ( N − 1 i − 1 ) h ( i , j ) g ( N − i , k − j ) h(N,k)=g(N,k)-\sum_{i=0}^{N-1}\sum_{j=0}^{k}\binom{N-1}{i-1}h(i,j)g(N-i,k-j) h(N,k)=g(N,k)−∑i=0N−1∑j=0k(i−1N−1)h(i,j)g(N−i,k−j)(容斥原理,枚举第一个顶点所在连通分量的大小),则连通二分图个数为h(N,k)/2。
于是
f
(
N
,
k
)
=
h
(
N
,
k
)
+
∑
i
=
0
N
−
1
∑
j
=
0
k
(
N
−
1
i
−
1
)
h
(
i
,
j
)
f
(
N
−
i
,
k
−
j
)
f(N,k)=h(N,k)+\sum_{i=0}^{N-1}\sum_{j=0}^{k}\binom{N-1}{i-1}h(i,j)f(N-i,k-j)
f(N,k)=h(N,k)+∑i=0N−1∑j=0k(i−1N−1)h(i,j)f(N−i,k−j)(枚举第一个顶点所在连通分量大小)
代码:
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
template<typename T>
constexpr T power(T a,i64 b){
T ans=1;
for(;b;b/=2){
if(b%2==1){
ans*=a;
}
a*=a;
}
return ans;
}
template<int P>
struct MInt{
constexpr MInt(): x{} {}
constexpr MInt(i64 x): x{norm(x%P)} {}
int x;
constexpr int norm(int x) const{
if(x<0){
return x+P;
}
return x;
}
constexpr int val() const{
return x;
}
constexpr MInt inv() const{
return power(*this,P-2);
}
constexpr MInt operator-() const{
MInt res;
res.x=P-x;
return res;
}
constexpr MInt& operator+=(const MInt& rhs){
x=(x+rhs.x)%P;
return *this;
}
constexpr MInt& operator-=(const MInt& rhs){
x=norm(x-rhs.x);
return *this;
}
constexpr MInt& operator*=(const MInt& rhs){
x=(1ll*x*rhs.x)%P;
return *this;
}
constexpr MInt& operator/=(const MInt& rhs){
return *this*=rhs.inv();
}
friend constexpr MInt operator+(const MInt& lhs,const MInt& rhs){
MInt res=lhs;
res+=rhs;
return res;
}
friend constexpr MInt operator-(const MInt& lhs,const MInt& rhs){
MInt res=lhs;
res-=rhs;
return res;
}
friend constexpr MInt operator*(const MInt& lhs,const MInt& rhs){
MInt res=lhs;
res*=rhs;
return res;
}
friend constexpr MInt operator/(const MInt& lhs,const MInt& rhs){
MInt res=lhs;
res/=rhs;
return res;
}
friend std::istream& operator>>(std::istream& is,MInt& a){
i64 v;
is>>v;
a=MInt(v);
return is;
}
friend std::ostream& operator<<(std::ostream& os,const MInt& a){
os<<a.val();
return os;
}
friend constexpr bool operator==(const MInt& lhs,const MInt& rhs){
return lhs.x==rhs.x;
}
friend constexpr bool operator!=(const MInt& lhs,const MInt& rhs){
return lhs.x!=rhs.x;
}
};
constexpr int P = 998244353;
using Z = MInt<P>;
struct Comb{
Comb():n{0},_fac{1},_invfac{1} {}
Comb(int m):Comb(){
init(m);
}
void init(int m){
m=min(m,P-1);
if(n>=m) return;
_fac.resize(m+1);
_invfac.resize(m+1);
_inv.resize(m+1);
for(int i=n+1;i<=m;i++){
_fac[i]=_fac[i-1]*i;
}
_invfac[m]=_fac[m].inv();
for(int i=m;i>n;i--){
_invfac[i-1]=_invfac[i]*i;
_inv[i]=_invfac[i]*_fac[i-1];
}
n=m;
return;
}
int n;
vector<Z> _fac,_invfac,_inv;
Z fac(int m){
if(m>n) init(2*m);
return _fac[m];
}
Z invfac(int m){
if(m>n) init(2*m);
return _invfac[m];
}
Z inv(int m){
if(m>n) init(2*m);
return _inv[m];
}
Z binom(int n,int m){
if(n<m||m<0) return 0;
return fac(n)*invfac(m)*invfac(n-m);
}
}comb;
int main(){
int n,m;
cin>>n>>m;
const int maxEdge=n*n/4+1;
vector<Z> b(maxEdge+1,0);
for(int i=1;i<=maxEdge;i++){
for(int j=0;j<=i;j++){
b[i]+=((i-j)%2?-1:1)*comb.binom(i,j)*power(Z(j),m);
}
}
vector<vector<Z>> g(n+1,vector<Z>(maxEdge+1,0));
for(int i=0;i<=n;i++){
for(int j=0;j<=maxEdge;j++){
for(int k=0;k<=i;k++){
g[i][j]+=comb.binom(i,k)*comb.binom(k*(i-k),j);
}
}
}
vector<vector<Z>> h(n+1,vector<Z>(maxEdge+1,0));
for(int i=0;i<=n;i++){
for(int j=0;j<=maxEdge;j++){
h[i][j]+=g[i][j];
for(int k=1;k<i;k++){
for(int l=0;l<=j;l++){
h[i][j]-=comb.binom(i-1,k-1)*h[k][l]*g[i-k][j-l];
}
}
}
}
vector<vector<Z>> f(n+1,vector<Z>(maxEdge+1,0));
for(int i=0;i<=n;i++){
for(int j=0;j<=maxEdge;j++){
f[i][j]+=h[i][j]/2;
for(int k=1;k<i;k++){
for(int l=0;l<=j;l++){
f[i][j]+=comb.binom(i-1,k-1)*h[k][l]/2*f[i-k][j-l];
}
}
}
}
Z ans=0;
for(int j=0;j<=maxEdge;j++){
ans+=f[n][j]*b[j]*power(Z(2),m);
}
cout<<ans<<'\n';
return 0;
}