题意:给定 n n n个点 m m m张图的有向图,有 1 ∼ m 1\sim m 1∼m互不相同每个点出度不超过 k k k。对于一个 k k k元组 c i c_i ci,图中的每个点 u u u只保留第 c d e g u c_{deg_u} cdegu小的边。求有多少种 c c c使得在保留下来的图中每个点沿着出边一直往下走可以走回自己。
n , m ≤ 2 × 1 0 5 , k ≤ 9 n,m\leq 2\times 10^5,k\leq 9 n,m≤2×105,k≤9
显然直接 k ! k! k!暴力枚举方案,问题在于如何快速判断。
不难看出题中的条件等价于每个点入度恰好为 1 1 1
也相当于每条边的终点恰好遍历 1 ∼ n 1 \sim n 1∼n
写个哈希就完了
复杂度 O ( n + k ! ) O(n+k!) O(n+k!)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <vector>
#include <utility>
#include <cstdlib>
#include <ctime>
#define MAXN 200005
using namespace std;
int u[MAXN],v[MAXN],n,m,k;
typedef pair<int,int> pi;
vector<int> e[MAXN];
vector<pi> lis[MAXN];
inline int id(const pi& p){return p.first*(p.first-1)/2+p.second;}
int ans[10],key[MAXN],val[10][10],rt,cnt;
void check()
{
int t=0;
for (int i=1;i<=k;i++) t+=val[i][ans[i]];
if (t==rt) ++cnt;
}
void dfs(int i)
{
if (i>k) return check();
for (ans[i]=1;ans[i]<=i;ans[i]++) dfs(i+1);
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for (int i=1;i<=n;i++) rt+=(key[i]=rand());
for (int i=1;i<=m;i++)
{
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
u[w]=x,v[w]=y;
}
for (int i=1;i<=m;i++) e[u[i]].push_back(v[i]);
for (int u=1;u<=n;u++)
for (int i=0;i<(int)e[u].size();i++)
val[e[u].size()][i+1]+=key[e[u][i]];
dfs(1);
cout<<cnt;
return 0;
}