AtCoder Regular Contest 176 C. Max Permutation(计数 分类讨论)

文章讨论了解决一个图论问题的方法,涉及判断有特定权值的边是否能构成有效路径,利用拓扑排序策略和限制条件计算可能的路径数量。关键步骤包括处理权值限制、确定自由点和更新计数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

思路来源

乱搞ac

题解

1. 如果有边的权值是1,意味着有两个点的权值都是1,无解

2. 如果一个点i被多个max条件控制,它的值不能超过这些max里最小的那个,记做up[i]

3. 如果同一个权值w对应的边不少于2条,这些边应该有一个公共点i,否则无解,如果up[i]<w也无解,否则给这个点i赋初值w

4. 从大到小考虑权值w,类似拓扑排序,把度为0的点加到队列里,初始没有被边覆盖的点是度为0的点,后续删掉max边之后且没有填值的点是度为0的点,当前度为0的点是自由点

检查所有权值w的边,对于当前边两个端点u,v

(1)如果w这个值已经被点i用了,u、v两个点中得有一个是i,否则无解

(2)如果没有用,考虑up[u]和up[v],如果都小于w,说明无解;如果正好有一个等于w,说明就该是那个点填w;否则有两个点等于w,说明都可以,把其中一个填w,剩下一个当自由点

从大到小考虑权值w的过程中,对于没有限制的,乘上当前自由点的个数,并令当前自由点数减1

其实就是在一个不断删除边的过程中统计数量

代码

//#include <bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
const int N=2e5+10,mod=998244353;
int n,m,a[N],b[N],c[N],up[N],res[N],to[N],deg[N];
vector<P>col[N];
map<int,int>mp;
bool used[N];
int main(){
    sci(n),sci(m);
    rep(i,1,n)up[i]=n+1;
    rep(i,1,m){
        sci(a[i]),sci(b[i]),sci(c[i]);
        deg[a[i]]++;deg[b[i]]++;
        up[a[i]]=min(up[a[i]],c[i]);
        up[b[i]]=min(up[b[i]],c[i]);
        col[c[i]].pb(P(a[i],b[i]));
    }
    if(!col[1].empty()){
        puts("0");
        return 0;
    }
    rep(i,2,n){
        if(!SZ(col[i]))continue;
        mp.clear();
        int mx=0;
        for(auto &x:col[i]){
            mp[x.fi]++;
            mp[x.se]++;
            mx=max(mx,mp[x.fi]);
            mx=max(mx,mp[x.se]);
        }
        int sz=SZ(col[i]);
        if(sz>=2){
            for(auto &x:mp){
                int pos=x.fi,cnt=x.se;
                if(cnt!=mx)continue;
                if(cnt!=sz){
                    puts("0");
                    return 0;
                }
                else{
                    if(up[pos]<i){
                        puts("0");
                        return 0;
                    }
                    res[pos]=i;
                    used[i]=1;
                    to[i]=pos;
                }
            }
        }
    }
    ll ans=1,cnt=0;
    rep(i,1,n){
        if(!deg[i])cnt++;
    }
    per(i,n,1){
        if(!SZ(col[i])){
            ans=1ll*ans*cnt%mod;
            cnt--;
            continue;
        }
        for(auto &x:col[i]){
            int u=x.fi,v=x.se;
            if(up[u]<i && up[v]<i){
                puts("0");
                return 0;
            }
            if(used[i]){
                if(res[u]!=i && res[v]!=i){
                    puts("0");
                    return 0;
                }
                deg[u]--;deg[v]--;
                if(!deg[u] && res[u]!=i)cnt++;
                if(!deg[v] && res[v]!=i)cnt++;
                continue;
            }
            if(up[u]==i && up[v]<i){
                res[u]=i;
                used[i]=1;
                deg[v]--;
                if(!deg[v])cnt++;
                continue;
            }
            if(up[v]==i && up[u]<i){
                res[v]=i;
                used[i]=1;
                deg[u]--;
                if(!deg[u])cnt++;
                continue;
            }
            ans=2ll*ans%mod;
            cnt++;
        }
    }
    printf("%lld\n",ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小衣同学

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

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

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

打赏作者

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

抵扣说明:

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

余额充值