线段有如下两类特点:
1 x y, 表示第 x 条线段和第 y 条线段相交 (相交在这里指至少有一个公共点)
2 x y,表示第 x 条线段在第 y 条线段的左边,且它们不相交。
共有 m 个特点,每个特点都是如上两类之一。
通过这些特点推理得到每条线段的端点。
x与y相交说明a[x]<b[y]且a[y]<b[x]
x在y左边说明b[x]<a[y]
每条线段x还应满足a[x]<b[x]
这相当于一个拓扑排序问题,小的数相当于安排在前面的任务
输出的第i个数就是第i个任务,那么a[1]尽可能小说明任务1要尽可能早做,b[1]尽可能小说明任务2要尽可能早做……
方法就是把DAG反向建边得到新图,在新图中求字典序最大的拓扑排序,再将这个排序反序输出就是满足要求的答案
IMP:
求一个字典序最小的拓扑排序,正确的做法并不是尽量把小的塞到前面,
而是把大的尽量塞到后面。
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<iostream> using namespace std; const int N = 300500; int point[N],to[N],next[N],cc; int dui[N],dcc; int ru[N]; int sc[N]; int n,m; void AddEdge(int x,int y) { cc++; next[cc]=point[x]; point[x]=cc; to[cc]=y; ru[y]++; } void Add(int x) { dcc++; int now=dcc; int next=now/2; while(next && dui[next]<x) { dui[now]=dui[next]; now=next; next=now/2; } dui[now]=x; } int Del() { int val=dui[1]; int now=1; int next=now*2; if(next+1<dcc && dui[next+1]>dui[next]) next++; while(next<dcc && dui[next]>dui[dcc]) { dui[now]=dui[next]; now=next; next=now*2; if(next+1<dcc && dui[next+1]>dui[next]) next++; } dui[now]=dui[dcc]; dcc--; return val; } int main() { int i,j; cin>>n>>m; while(m--) { int a,b,c; cin>>c>>a>>b; if(c==1) { AddEdge(a*2,b*2-1); AddEdge(b*2,a*2-1); } else AddEdge(b*2-1,a*2); } for(i=1; i<=n; i++) AddEdge(i*2,i*2-1); n*=2; for(i=1; i<=n; i++) { if(!ru[i]) Add(i); } for(i=n; i>=1; i--) { if(!dcc) { printf("Wrong "); return 0; } int now=Del(); int then=point[now]; while(then) { int tox=to[then]; ru[tox]--; if(!ru[tox]) Add(tox); then=next[then]; } sc[now]=i; } for(i=1; i<=n; i++) { cout<<sc[i]; if(i&1) cout<<' '; else cout<<endl; } return 0; }