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举例:
这是一张有向图,边表示一个约束,方向表示小于/小于等于的关系,边权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\) 如果有,则无解,如果没有就继续按照我们上面说的拓扑排序贪心即可
实现过程
- 根据条件,建有向图
- 跑 tarjan 然后我们按联通分量重构图,若联通分量内有 \(1\) 无解
- 拓扑排序,开始全为 1,从起点开始,遇到 \(1\) 加1,遇到 \(0\) 不管
- 最后检查一遍是否不超过 \(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;
}

浙公网安备 33010602011771号