E

题:https://codeforces.com/contest/1395/problem/E

题意:给定n个点m条边的图,给定k,每个点的出度不会超过k,定义k个元素的ci数组:对于每个节点度数di,那么节点 i 要走第cdi小边权的边。对于图上的每个点作为起点进行若干步ci数组操作都能回到起点,问这样的ci数组有多少个。

分析:由于1<=k<=9&&1<=ci<=i,那么最多有9!种ci数组,即我们可以通过枚举来判断其是否满足条件。

   其实我们只要考虑每个点的下一个点会到哪个点即可,那么要求每个点出发都能回到原点的条件就转化为每个点的下一个点组成的点集为1~n(1到n恰好都只出现一次)。

   那么我们考虑用哈希值来表示点集,对于每个度数值有若干个点,累加每个第 i 小值节点对应的哈希值;

   因为哈希值的计算满足结合律,所以直接把枚举的ci数组的哈希值与1~n和哈希值比较即可完成判断。

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define ull unsigned long long
#define pii pair<int,int>
#define MP make_pair
const int M=2e5+5;
const int H=1e9+7;
vector<pii >g[M];

ull goal,a[20][20],Hash[M];
int n,m,k,ans;
void dfs(int u,ull now){
    if(u==k+1){
        ans+=(now==goal);
        return ;
    }
    for(int i=1;i<=u;i++)
        dfs(u+1,now+a[u][i]);

}
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int u,v,w,i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&w);
        g[u].pb(MP(w,v));
    }
    Hash[0]=1,goal=0;
    for(int i=1;i<=n;i++){
        Hash[i]=Hash[i-1]*H;
        goal+=Hash[i];
    }

    for(int i=1;i<=n;i++){
        sort(g[i].begin(),g[i].end());
        int tot=g[i].size();
        for(int j=0;j<tot;j++)
            a[tot][j+1]+=Hash[g[i][j].second];
    }
    ans=0;
    dfs(1,0);
    printf("%d
",ans);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/starve/p/13495004.html