agc046_f Forbidden Tournament
agc046_f Forbidden Tournament
https://atcoder.jp/contests/agc046/tasks/agc046_f
Tutorial
称条件中禁止出现的子图为\(H\).
首先,考虑若\(G\)中存在一个入度为\(0\)的点\(v\),那么可以将\(v\)删去,剩下一个\(K-1\)的子问题.所以接下来只讨论\(G\)中所有点都有至少一条入边的情况.
随意选择\(G\)中的某个点\(v\),设所有存在\(v\to w\)的\(w\)的集合为\(Y\),令\(X=V(G) \setminus Y\)(注意\(v\)在\(X\)之中).假如\(X\)中存在一个环,那么一定存在一个三元环(竞赛图性质).而且由于\(X\)中其他点都是指向\(v\)的,所以\(v\)一定不在这个三元环内,那么这个三元环和\(v\)就组成了\(H\).所以\(X\)中一定存在拓扑序\(x_1,x_2,\cdots,x_k\),且\(u=x_k\),满足对于\(i<j\),\(x_i \to x_j\)存在.
由于所有点入度不为\(0\),所以一定存在某条边\(w\to u\),其中\(w \in Y,v \in X\),再考虑某个存在\(w \to w'\)的\(w'\),此时的情况如[图1].那么为了不出现\(H\),边\(w'\to u\)一定存在.那么如果\(w\)可以到达的点的集合中存在环,那么某个三元环一定和\(u\)组成了\(H\)[图2].而且如果\(w\)不能到达的集合中存在环,那么某个三元环一定和\(w\)组成了\(H\)[图3].所以\(Y\)中也不存在环,所有有拓扑序\(y_1,\cdots,y_l\)满足对于\(i<j\),\(y_i\to y_j\)存在.
\(X,Y\)中边的方向确定了,现在考虑\(X,Y\)之间的边的情况.根据之前的结论,若\(y_j \to x_i\)存在,那么对于\(j' \in [j,l]\),\(y_{j'}\to x_i\)也一定存在.所以如果\(x_1 \to y_l\)存在,那么对于所有\(j\in[1,l]\),\(x_1 \to y_j\)存在,那么\(x_1\)的入度就是\(0\),矛盾.所以\(y_l \to x_1\)一定存在.
对于\(2 \le i \le k\),假设\(x_i \to y_l\)存在.如果对于某个\(i'>i\),存在\(y_l \to x_i'\),那么\(x_i,x_i',y_l,x_1\)就构成了\(H\)[图4].所以,若\(x_i \to y_l\)存在,那么\(x_{i'}\to y_l\)也存在.设\(t\)为最大的数使得\(y_l\to x_t\)存在,设\(i \in [1,t],j \in [1,l-1]\),且\(x_i \to y_j\)存在.那么\(x_i,y_j,y_l\)构成了三元环,且对于\(i<i' \le k\),要么\(x_{i'} \to y_j\)存在,要么\(x_{i'}\to y_l\)存在,否则图中将包含\(H\)[图5].而且根据上一段的结论,若存在\(y_j\to x_{i'}\),那么\(y_l \to x_{i'}\)也会存在,所以一定存在\(x_{i'}\to y_j\).所以若存在\(y_j \to x_i\),那么对于所有\(i'<i\),\(y_j \to x_{i'}\)存在.
考虑一个\(k \times l\)的网格图,若存在\(y_j \to x_i\),则将\((i,j)\)涂黑.那么根据以上图的性质,可以得到这个网格图的某些性质
- 若\((i,j)\)是黑色的,则对于所有\(i'<i\),\((i',j)\)也是黑色的.
- 若\((i,j)\)是黑色的,则对于所有\(j'>j\),\((i,j')\)也是黑色的.
- \((1,l)\)是黑色的.
- 对于所有\(j\),\((k,j)\)是白色的.(\(v=x_k\))
其中入度\(\le K\)的条件相当于某个阶梯型的区域不能是黑色.
则方案数可以用一个\(O(kl)\)的DP算出,令\(v=1\),枚举\(|X|\),\(O(n)\),\(x_1,x_2,\cdots,x_k,y_1,y_2,\cdots y_l\)的方案数可以最后乘以\((n-1)!\)表示,最初还要枚举删去的节点数.
总复杂度\(O(n^4)\)
Code
https://atcoder.jp/contests/agc046/submissions/15081967
#include <cstdio>
#include <cstring>
#include <iostream>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define inver(a) power(a,mod-2)
using namespace std;
inline char gc() {
return getchar();
static char buf[100000],*l=buf,*r=buf;
return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
}
template<class T> void rd(T &x) {
x=0; int f=1,ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=gc();}
x*=f;
}
typedef long long ll;
const int maxn=200+5;
int n,K,mod;
int fac[maxn],inv[maxn],C[maxn][maxn];
int dp[maxn][maxn];
inline int add(int x) {return x>=mod?x-mod:x;}
ll power(ll x,ll y) {
ll re=1;
while(y) {
if(y&1) re=re*x%mod;
x=x*x%mod;
y>>=1;
}
return re;
}
int sol(int k,int l,int K) {
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(int j=1;j<=l;++j) {
int sum=0;
for(int i=0;i<k;++i) {
sum=add(sum+dp[i][j-1]);
if((l-j+1)+(i-1)<=K&&(k-i)+(j-1)<=K) dp[i][j]=sum;
}
}
int an=0;
for(int i=1;i<k;++i) an=add(an+dp[i][l]);
return an;
}
int sol(int n,int K) {
if(n==1) return 1;
int an=0;
for(int i=1;i<=K+1;++i) an=add(an+sol(i,n-i,K));
an=(ll)an*fac[n-1]%mod;
// debug("%d %d %d\n",n,K,an);
return an;
}
void init() {
fac[0]=1;
for(int i=1;i<=n;++i) fac[i]=(ll)fac[i-1]*i%mod;
inv[n]=inver(fac[n]);
for(int i=n;i>=1;--i) inv[i-1]=(ll)inv[i]*i%mod;
for(int i=0;i<=n;++i) {
C[i][0]=C[i][i]=1;
for(int j=1;j<i;++j) C[i][j]=add(C[i-1][j-1]+C[i-1][j]);
}
}
int main() {
rd(n),rd(K),rd(mod);
init();
int an=0;
for(int i=0;i<=K;++i) an=(an+(ll)sol(n-i,K-i)*C[n][i]%mod*fac[i])%mod;
printf("%d\n",an);
return 0;
}