CodeForces #664E - Boboniu Walks on Graph

传送门

题意

给定一个n个点m条边的有向图,每一个点的出度最多为k,然后给你一个跑图规则数组ci,对于出度为x的点,跑当前点第cx小的边。问你有多少种数组c,可以使得对于任何点,总能沿着数组c跑,跑回该点。

思路

根据规则转换一下,对于所有点,它的出边都是固定的。所以每一个都在且仅在一个有向环内,即所有点都仅经过一次。可以看到k<=9,因此我们枚举所有的c数组,符合条件的计上。跑图的过程用点权hash来计算。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
#define maxn 100005
#define mod 1000000007
ll s[maxn<<1][2],node[maxn<<1],sum[10][10],n,m,k,per[maxn],ans=0,res=0;
vector<ll> G[maxn<<1];
int vis[maxn];
void check(){
    ll cnt=0;
    for(int i=1;i<=k;i++)
        cnt+=sum[i][per[i]];
    if(ans==cnt) res++;
}
void dfs(int x){
    if(x>k){
        check();
		return; 
    }
    for(int i=1;i<=x;i++){
		per[x]=i;
	    dfs(x+1);
	}
	return;
}

int main(){
    ll i,j,t,u,v,w;
    scanf("%lld%lld%lld",&n,&m,&k);
    for(i=1;i<=m;i++){
        scanf("%lld%lld%lld",&u,&v,&w);
        s[w][0]=u,s[w][1]=v;
    }
    for(i=1;i<=m;i++) G[s[i][0]].push_back(s[i][1]);
    for(i=1;i<=n;i++) node[i]=rand()%mod,ans+=node[i];
    for(i=1;i<=n;i++)
        for(j=0;j<G[i].size();j++)
            sum[G[i].size()][j+1]+=node[G[i][j]];
    dfs(1);
    printf("%lld\n",res);
}