Codeforces Round #664 (Div. 1) B.Boboniu Walks on Graph(图论/暴力枚举)

题目

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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值