题目
n(n<=2e5)个点,m(m<=2e5)条边的有向图,每个点的出度最多为k(k<=9),
m条边每条边都有一个权值,m条边构成了1-m的一个排列,
求合法的k元组(c1,...,ck)的数量,
使得任意出度为i的点沿着第ci小的权值走向下一个点时,能遍历全图
思路来源
disangan233代码
题解
注意到ci<=i,所以所有k元祖数量级是9!的,能遍历全图,说明所有点构成了一个环
每个点的入度和出度都恰好为1,先检查出度,如果存在出度为0的直接输出0
然后考虑入度,每个点v只能取一条边指向v,
所以,如果存在x->z和y->z两条边,x和y的度数相同,
x->z边权排名和y->z边权排名也相同,肯定构不成环,则该排名必不可取
具体实现,now[v][deg][rk]>=2,则b[deg][rk]=1,表示该度对应的排名不可取
此外,如果x->z和y->z排名不同,或者x和y度数不同,则两条边是互斥的,不能同时取,
用c[degx][rkx][degy][rky]记录一下
暴力枚举全排列种方案,最后用O(k^2)检查一下,
是否存在被禁止的b情况和被禁止的c情况,
总复杂度O(m*k+k!*k*k)
代码1
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> P;
const int N=2e5+10;
int ans,n,m,k,u,v,w,now[N][10][10];
int b[10][10],c[10][10][10][10],a[10];
vector<P>e[N];
void dfs(int x){
if(x==k+1){
for(int i=1;i<=k;++i){
if(b[i][a[i]])return;
}
for(int i=1;i<=k;++i){
for(int j=i+1;j<=k;++j){
if(c[i][a[i]][j][a[j]])return;
}
}
ans++;
return;
}
for(int i=1;i<=x;++i){
a[x]=i;
dfs(x+1);
}
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;++i){
scanf("%d%d%d",&u,&v,&w);
e[u].push_back(P(w,v));
}
for(int i=1;i<=n;++i){
if(!e[u].size()){
puts("0");
return 0;
}
sort(e[i].begin(),e[i].end());
int deg=e[i].size();
for(int j=0;j<deg;++j){
int v=e[i][j].second;
now[v][deg][j+1]++;
}
}
for(int i=1;i<=n;++i){
int deg=e[i].size();
for(int j=0;j<deg;++j){
int v=e[i][j].second;
if(now[v][deg][j+1]>=2){
b[deg][j+1]=1;
//printf("b %d %d\n",deg,j+1);
continue;
}
for(int d=1;d<deg;++d){
for(int e=1;e<=d;++e){
if(now[v][d][e]==1){
c[d][e][deg][j+1]=1;
//printf("c (%d,%d)(%d,%d)\n",d,e,deg,j+1);
}
}
}
}
}
dfs(1);
printf("%d\n",ans);
return 0;
}
代码2
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> P;
#define pb push_back
#define fi first
#define se second
const int N=2e5+10;
int ans,n,m,k,u,v,w,now[N][10][10];
int b[10][10],c[10][10][10][10],a[10];
vector<P>e[N];
void dfs(int x){
if(x==k+1){
for(int i=1;i<=k;++i){
if(b[i][a[i]])return;
}
for(int i=1;i<=k;++i){
for(int j=i+1;j<=k;++j){
if(c[i][a[i]][j][a[j]])return;
}
}
ans++;
return;
}
for(int i=1;i<=x;++i){
a[x]=i;
dfs(x+1);
}
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;++i){
scanf("%d%d%d",&u,&v,&w);
e[u].push_back(P(w,v));
}
for(int i=1;i<=n;++i){
if(!e[u].size()){
puts("0");
return 0;
}
sort(e[i].begin(),e[i].end());
int deg=e[i].size();
for(int j=0;j<deg;++j){
int v=e[i][j].second;
now[v][deg][j+1]++;
}
}
for(int i=1;i<=n;++i){
vector<P>ok;
for(int j=1;j<=k;++j){
for(int l=1;l<=j;++l){
if(now[i][j][l]>=2){
b[j][l]=1;
continue;
}
if(now[i][j][l]==1){
ok.push_back(P(j,l));
}
}
}
int sz=ok.size();
for(int j=0;j<sz;++j){//只有边数>=45的点才会45*45,否则为sz*sz,则复杂度最坏(m/45)*45*45
for(int l=j+1;l<sz;++l){
c[ok[j].fi][ok[j].se][ok[l].fi][ok[l].se]=1;
}
}
}
dfs(1);
printf("%d\n",ans);
return 0;
}