T2
题解:
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反向建边得到新图,在新图中求字典序最大的拓扑排序,再将这个排序反序输出就是满足要求的答案
然而在考场上我首先写出了一个小根堆,建立正向边,求字典序最小的拓扑排序,过了样例,然而我随便的一组小数据就挂掉了
4 1
2 4 2
建的图是这样的
根据我们次次找最小的原理,得到的答案是这样的
1 2
7 8
3 4
5 6
这样字典序最小吗?然而字典序最小的是这样的
1 2
5 6
7 8
3 4
为什么会这样,因为有一些编号小但受约束所以拓扑序靠后的点。
怎么解决?其实只需要大根堆,建立反向边,求字典序最大的拓扑排序
为什么呢?
在DAG中,存在一些编号较大但是拓扑序靠前的点,也存在编号较小但是受约束而拓扑序靠后的点
如果我们正序进行,那些拓扑序靠前但是编号较大的点会受编号的限制而排在后面,从而影响之后的答案
如果逆序进行,拓扑序靠前且编号大的点会尽量靠后,反过来之后就会尽量靠前了
或者说,那些编号较小但是拓扑序较大的点,在逆序的时候会尽量靠后,这样反过来就可以满足:拓扑序虽然大,但是由于编号小,尽可能靠前安排
代码:
#include <cstdio>
#include <iostream>
using namespace std;
const int N=100005;
struct hh{int a,b;}se[N];
int q[N*2],cnt;
void push(int x)
{
q[++cnt]=x;
int fa=cnt>>1,now=cnt;
while (fa&&q[fa]<q[now])
{
swap(q[fa],q[now]);
now=fa; fa>>=1;
}
}
void del()
{
q[1]=q[cnt--];
int now=1,l=now<<1,r=now<<1|1,better=l;
if (r<=cnt && q[r]>q[l]) better=r;
while (better<=cnt && q[now]<q[better])
{
swap(q[now],q[better]);
now=better; l=now<<1; r=now<<1|1;
better=l;
if (r<=cnt && q[r]>q[l]) better=r;
}
}
int ans[N*2],tot,nxt[N*2],point[N*2],v[N*2],du[N*2];bool vis[N*2];
void addline(int x,int y){++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;}
int main()
{
freopen("seg.in","r",stdin);
freopen("seg.out","w",stdout);
int n,m;
scanf("%d%d",&n,&m);
int num=0;
for (int i=1;i<=n;i++) se[i].a=++num,se[i].b=++num;
for (int i=1;i<=m;i++)
{
int id,x,y;
scanf("%d%d%d",&id,&x,&y);
if (id==1) du[se[y].a]++,addline(se[x].b,se[y].a),du[se[x].a]++,addline(se[y].b,se[x].a);
else du[se[x].b]++,addline(se[y].a,se[x].b);
}
for (int i=1;i<=n;i++) du[se[i].a]++,addline(se[i].b,se[i].a);
for (int i=1;i<=n*2;i++)
if (!du[i]) push(i);
for (int i=n*2;i>=1;i--)
{
if (!cnt) {printf("Wrong\n");return 0;}
int now=q[1]; del(); ans[now]=i;
for (int j=point[now];j;j=nxt[j])
{
du[v[j]]--;
if (!du[v[j]]) push(v[j]);
}
}
for (int i=1;i<=n*2;i+=2) printf("%d %d\n",ans[i],ans[i+1]);
}