abc478_E-lt and le 解题报告(tarjan)

abc478_E-lt and le 解题报告(tarjan)

我绝对不会告诉你我没做 A~D

1. 原题 abc478_E-lt and le

题目描述

请判断是否存在一个长度为 \(N\) 的整数序列 \(A\),其中每个元素都在 \(1\) 到 \(N\)(含两端)之间,并且满足以下全部 \(Q\) 个条件;如果存在,请输出其中任意一个。

第 \(i\) 个条件(\((1\le i\le Q)\))由三个整数 \((t _ i,u _ i,v _ i)\) 给出,具体含义如下:

  • 当 \(t _ i=0\) 时:\(A _ {u _ i}\le A _ {v _ i}\)
  • 当 \(t _ i=1\) 时:\(A _ {u _ i}\lt A _ {v _ i}\)

如果符合条件的序列不止一个,输出任意一个即可。

约束条件

  • \(1\le N\le2\times10 ^ 5\)
  • \(1\le Q\le2\times10 ^ 5\)
  • \(t _ i\in\lbrace0,1\rbrace\ (1\le i\le Q)\)
  • \(1\le u _ i\le N\ (1\le i\le Q)\)
  • \(1\le v _ i\le N\ (1\le i\le Q)\)
  • 所有输入值均为整数。

输入

输入从标准输入按以下格式给出:

\(N\) \(Q\)
\(t _ 1\) \(u _ 1\) \(v _ 1\)
\(t _ 2\) \(u _ 2\) \(v _ 2\)
\(\vdots\)
\(t _ Q\) \(u _ Q\) \(v _ Q\)

输出

第一行输出 Yes,表示存在满足条件的整数序列;如果不存在,则输出 No。 如果第一行输出了 Yes,请在第二行输出一个满足所有条件的整数序列,各元素之间用空格分隔。 如果符合条件的序列有多个,输出任意一个都可以。

样例1

Input Output
7 8
0 1 4
0 2 6
0 3 2
1 3 5
0 4 1
0 4 3
1 5 7
0 6 7
Yes
1 4 2 1 3 5 6

令 \(A=(1,4,2,1,3,5,6)\)。这是一个长度为 \(7\) 的整数序列,且所有元素都在 \(1\) 到 \(7\)(含两端)之间。 例如,第一个条件 \(A _ 1\le A _ 4\) 成立,因为 \(1\le 1\)。 其余七个条件同样成立,因此输出 \(A=(1,4,2,1,3,5,6)\) 会被判定为正确。

此外,\(A=(1,1,1,1,2,2,3)\)、\(A=(3,5,4,3,6,7,7)\) 等序列也都满足条件,输出它们同样正确。

样例2

Input Output
1 1
1 1 1
No

不存在满足 \(A _ 1\lt A _ 1\) 的整数序列 \(A\)。

样例3

Input Output
见原题
见原题

2. 题意概述

构造一个长度为 \(n\) 数组 \(A\),使得满足给定的 \(q\) 个约束条件,即:

对于 \(\forall 1 \le i \le q\),\( \begin{cases} x_i \le y_i & \text{if } t_i = 0 \\ x_i \lt y_i & \text{if } t_i = 1 \end{cases} \), \(1 \le x_i, y_i \le n\), \(t_i \in [0, 1]\)

且 \(\forall x \in A\) 需满足 \(1 \le x \le n\)

3. 题解

题目分析

首先看到这个题,我们可以想到一个非常暴力 \(O(n^n)\) 的做法,这显然最多只能过 \(n \le 9\) 的数据,既然从 构造数组再判断合法 的角度无法思考问题,那我们从 满足条件再构造数组 的角度来解决,我们把这些约束写成一串会发现这很像一个图,那么我们就成功将条件转化为了一个图上的问题

拿样例1举例:样例1

这是一张有向图,边表示一个约束,方向表示小于/小于等于的关系,边权0/1表示小于/小于等于

我们贪心的想,我们希望数组里的数从 \(1\) 开始到 \(n\),那么我们开始让所有点都是 \(1\),然后从一个起点开始走下一个节点,遇到边权为 \(0\) 不管,遇到边权为 \(1\) 则给下一个节点加一,这样可以满足第一个条件,最后我们回头检查一遍构造的数组,如果超过 \(n\) 则无解

那就又有一个问题了,我们起点怎么找?拓扑排序呗,有环怎么办?tarjan!

如何tarjan?

不会的去学:tarjan

我们发现一个强联通分量之间约束必须是小于等于,很简单,反证,1 -> 2 边权为0,2 -> 1 边权为1,\(a_1 \le a_2\) 与 \(a_2 \lt a_1\) 无法同时满足。我们还发现一个联通分量内的构造值必须相等(易证),那么我们先跑 tarjan,最后重构图的时候判断一个联通分量内是否有 \(1\) 如果有,则无解,如果没有就继续按照我们上面说的拓扑排序贪心即可

实现过程

  1. 根据条件,建有向图
  2. 跑 tarjan 然后我们按联通分量重构图,若联通分量内有 \(1\) 无解
  3. 拓扑排序,开始全为 1,从起点开始,遇到 \(1\) 加1,遇到 \(0\) 不管
  4. 最后检查一遍是否不超过 \(n\) 若超过,无解,不超过输出即可

代码示例

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<set>
#include<map>
#include<unordered_map>
#include<queue>
#define int long long

using namespace std;

const int N = 1e6 + 10;
const int mod = 1e9 + 7;
const double eps = 1e-9;
typedef long long LL;
typedef unsigned long long ULL;
typedef double db;
typedef pair<int, int> PII;

vector<PII> e[N], ee[N];

int dfn[N], low[N], tot;
bool st[N];
int stk[N], tt = 0;

int cnt = 0;
int g[N], id[N];

void tarjan(int u){
    dfn[u] = low[u] = ++ tot;
    stk[ ++ tt] = u;
    st[u] = true;

    for(auto it : e[u]){
        int j = it.first, dist = it.second;
        if(!dfn[j]){
            tarjan(j);
            low[u] = min(low[u], low[j]);
        }
        else if(st[j]){
            low[u] = min(low[u], dfn[j]);
        }
    }

    if(low[u] == dfn[u]){
        cnt ++;
        int y;
        do{
            y = stk[tt --];
            st[y] = false;
            id[y] = cnt;
        }while(u != y);
    }
}

int d[N];
int q[N];
int ans[N];

void top_sort(){
    int hh = 1, tt = 0;
    for(int i = 1 ; i <= cnt ; i ++) if(!d[i]) q[ ++ tt] = i, ans[i] = 1;
    
    while(hh <= tt){
        int u = q[hh ++];
        for(auto it : ee[u]){
            int j = it.first, dist = it.second;
            if(dist) ans[j] = max(ans[u] + 1, ans[j]);
            else ans[j] = max(ans[u], ans[j]);
            if(-- d[j] == 0){
                q[ ++ tt] = j;
            }
        }
    }
}

signed main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int n, m;
    cin>>n>>m;

    for(int i = 1 ; i <= m ; i ++){
        int a, b, c;
        cin>>c>>a>>b;
        e[a].push_back({b, c});
    }

    for(int i = 1 ; i <= n ; i ++){
        if(!dfn[i]) tarjan(i);
    }

    for(int i = 1 ; i <= n ; i ++){
        for(auto it : e[i]){
            int j = it.first, dist = it.second;
            int a = id[i], b = id[j];
            if(a != b){
                ee[a].push_back({b, dist});
                d[b] ++;
            }
            else if(dist){
                cout<<"No\n";
                return 0;
            }
        }
    }

    top_sort();

    for(int i = 1 ; i <= cnt ; i ++){
        if(ans[i] > n || ans[i] < 1){
            cout<<"No\n";
            return 0;
        }
    }

    cout<<"Yes\n";
    for(int i = 1 ; i <= n ; i ++){
        cout<<ans[id[i]]<<' ';
    }

    return 0;
}
posted @ 2026-10-05 09:40  神烦doge  阅读(7)  评论(1)    收藏  举报