【还未找到原题】宝石(GEM)
题意
给定 \(m\) 对关系,表示 \(a\) 比 \(b\) 小,此时问最先确定每一个点的排名的关系最小编号,如果最后还未确定排名,则此点输出-1。
由于没有原题,给个样例:
input:
4 4
2 4
3 1
4 1
2 3
output:
3 4 -1 -1
思路
首先容易想到建有向图,边权就是每一条边的编号。
考虑对于一个确定排名的点 \(u\),要确定它后面的点都比它大,则需要求它到每一个它后面的点的边权最大值,最后在所有它后面的点取个 \(max\) 就行。
但这样显然超时,考虑到每一个 \(u\) 编号后面的点中所有是由排名大于 \(u\) 的点指向另一个排名大于 \(u\) 的点的边都有可能成为答案边。所以可以简化成对于一个点 \(v\) 且 \(v\) 在 \(u\) 后面,排名不小于 \(u\) 的点指向 \(v\)的取个最小值,最后所有不小于 \(u\) 的点全部取个最大值就可以算出其中一半的答案,这里明显可以用两个 \(set\) 来维护,一个维护每个点的,一个维护整体的。
然后倒着连反边再做一遍可以算出前面的。
code
#include<bits/stdc++.h>
#define ll int
#define pll pair<ll,ll>
#define x first
#define y second
#define mp make_pair
using namespace std;
const int N = 2e5+5,M = 8e5+5;
struct edge{
ll u,v;
}g[M];
struct Edge{
int to,nxt;
int w;
}e[M];
ll n,m;
int head[N];
int tot;
void add_Edge(int u,int v,int w){
e[tot].to=v,e[tot].nxt=head[u];
e[tot].w=w;
head[u]=tot++;
}
ll du[N];
ll res[N],f[N];
set<ll> st[N],p;
void init(){
tot=0;
memset(du,0,sizeof(du));
memset(head,-1,sizeof(head));
}
void solve(){
init();
for(int i=1;i<=m;i++){
ll u=g[i].u,v=g[i].v;
add_Edge(u,v,i);
st[v].insert(i);
}
queue<ll> q;
for(int i=1;i<=n;i++){
if(st[i].empty())q.push(i);
else p.insert(*st[i].begin());
}
for(int i=1;i<=n;i++){
ll u=q.front();
q.pop();
if(q.empty()){
res[u]+=n-i;
if(!p.empty())f[u]=max(f[u],*p.rbegin());
}
for(int j=head[u];~j;j=e[j].nxt){
int v=e[j].to,w=e[j].w;
p.erase(*st[v].begin());
st[v].erase(w);
if(!st[v].empty())p.insert(*st[v].begin());
else q.push(v);
}
}
}
int main() {
freopen("gem.in","r",stdin);
freopen("gem.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++)
cin>>g[i].u>>g[i].v;
solve();
for(int i=1;i<=m;i++)
swap(g[i].u,g[i].v);
solve();
for(int i=1;i<=n;i++){
if(res[i]!=n-1)cout<<"-1 ";
else cout<<f[i]<<" ";
}
return 0;
}

浙公网安备 33010602011771号