zoukankan      html  css  js  c++  java
  • 【*篇】2.28测试T2 线段 拓扑排序

    题目:


    思路:
    看到这种找前后的题目... 第一反应就是拓扑排序_(:з」∠)_
    每条线段都有左右两个端点咯, 然后就乱搞吧..
    我们用(i)(i')分别表示第(i)条线段的左右端点..
    然后如果(x)(y)的左边, 那么(x')一定小于(y), 我们就建一条(x'->y)的边
    如果(x)(y)相交, 那么一定(x<y')(y<x')(显然), 我们就分别建(x->y')(y->x')两条边.
    然后对于每个点(x), (x<x'), 那么再建(x->x')的边...
    然后为了字典序, 把编号扔进一个小根堆里维护一下, 按照堆的顺序跑一边拓扑排序大约就可以了吧..
    Wrong的情况也很好特判, 只要判断进队的点的个数不到(2n)就好了...
    轻松加愉快地过了样例. 于是这样就做完了?


    然后非常无耻地找了一下数据一测, 发现得到了10pts... 就是输出Wrong的10分...
    然后发现顺序全错了...这就很尴尬...
    那就打开组数据看一下嘛, 反正(nleq10)的数据很适合手玩..
    然后第二组数据((n)最小的一组)是这样的:

    7 4
    2 1 7
    2 2 7
    1 7 5
    2 4 2
    

    我们来画一下:
    就可以看出问题了. 如果我们这么做, (x)进堆之前, 若(exists)(y->x)(exists u满足x<u<y), 则拓扑序列将变为(u y x)而不是(y x u), 字典序就不优了..(看清楚题目的字典序指的是什么..)
    那怎么办啊? 为了解决这个问题, 我们考虑把边都反向, 堆变成一个大根堆, 然后顺序从后向前做(就是最后把拓扑序列倒过来), 就可以了..
    因为我们发现, 这么一搞, 存在依赖的编号较小的点将会比较靠后的弹出, 倒过来就变成优先了, 也就满足了字典序的要求..
    然后想到这一点就没什么难度了...
    std好像用了手写堆, 但是我人懒就直接上priority_queue咯, 不过跑的还是飞快, 最大的点也用不到0.3s..
    代码:

    #include <cstdio>
    #include <cstring>
    #include <queue>
    using namespace std;
    const int N=202020;
    priority_queue<int> q;
    inline int gn(int a=0,char c=0){
    	for(;c<'0'||c>'9';c=getchar());
    	for(;c>47&&c<58;c=getchar())a=a*10+c-'0';
    return a;}
    struct edge{int to,next;}e[N];
    int v[N],du[N],ans[N],tot,cnt,n,m;
    inline void buildedge(int x,int y){
    	++du[y]; e[++tot].to=y; e[tot].next=v[x]; v[x]=tot;
    }
    int main(){
    	freopen("seg.in","r",stdin); freopen("seg.out","w",stdout);
    	n=gn(),m=gn();
    	for(int i=1;i<=n;++i) buildedge(i*2,i*2-1);
    	for(int i=1;i<=m;++i){
    		int x=gn(),y=gn(),z=gn();
    		if(x==1) buildedge(z*2,y*2-1),buildedge(y*2,z*2-1);
    		else buildedge(z*2-1,y*2);
    	}n<<=1;
    	for(int i=1;i<=n;++i)
    		if(!du[i]) q.push(i); cnt=n;
    	while(!q.empty()){
    		int x=q.top(); q.pop();
    		for(int j=v[x];j;j=e[j].next){
    			--du[e[j].to];
    			if(du[e[j].to]==0)
    				q.push(e[j].to);
    		}
    		ans[x]=cnt--;
    	}
    	if(cnt) puts("Wrong");
    	else for(int i=1;i<=n;i+=2)
    		printf("%d %d
    ",ans[i],ans[i+1]);
    }
    
  • 相关阅读:
    selenium 模拟键盘事件 复制粘贴、右键、回车等
    02安卓用户界面优化之(二)SlidingMenu使用方法
    02Android用户界面优化之(一)Android Fragment
    (九)Android权限系统
    Android SDK 在线更新镜像服务器资源
    (八)Android广播接收器BroadcastReceiver
    (七)Android中AIDL的应用与理解
    (六)Android中Service通信
    (五)认识Android中的Service
    Gradle中文乱码
  • 原文地址:https://www.cnblogs.com/enzymii/p/8483576.html
Copyright © 2011-2022 走看看