【BZOJ2584】memory,扫描线+拓扑图+骗

来源:互联网 发布:最霸气的帝王诗 知乎 编辑:程序博客网 时间:2024/05/21 05:43

传送门
思路:
神题
总结——写了一个错误做法,然后A了
这里写图片描述
想+写+调搞了将近一个星期
一开始想的是把斜着的线段转化成竖着的线段和横着的线段,然后分别做
但是好不容易打完以后,发现轻易就有反例(?)
然后先把题弃了,做了些简单题
学了圆的扫描线回来,发现这个题是可以用到的类似的思路,因为都是不相交的图形,可以通过交点的横纵坐标扔进set里搞一搞。
每添加进一个线段,就由它向它的前驱连边,由后继向它连边
每删除一个线段,就由它的后继向前驱连边
边数是O(n)
由于上下移动,左右移动正好是相反的,所以分别拿x,y为关键字扫两次就能建出4个拓扑图来了
关于第二问,显然只往一个方向移动肯定是有解的,而且这些线段的关系是一个DAG
然后就建了四个方向的DAG……
处理线段端点相交的话,坐标相等时再按照左右端点为关键字选就可以了
过了样例后交了上去,结果
这里写图片描述
最上边的两次是暴力向前驱后继连边,边数为O(n2)
要来数据调,发现第2,3个点的第一问都过不了
这里写图片描述
那怎么跑的900ms+?
然后发现set出现了玄学的问题
yveh,ISA,DaD3zZ都过来看错误
最后yveh指出我的cmp函数竟然可以满足a<b,b>a同时成立
同时我搞了个dcmp,再测试就过了
这里写图片描述
用数据测了下,第1问是没问题了,但是第二问应该依然是错误的
可是仔细思考一下,我发现我的做法是对于每个合法操作,在各个拓扑图里暴力删线段,这样会使在某一次操作中,可能图中还有限制当前移动操作的线段,但由于不是预处理时的前驱,就在图中没有连边,导致本来非法的操作被判断为合法
这个bug影响第一问,但应该是不会影响第二问的,因为我们只要按照拓扑排序的顺序进行输出,得到的操作一定是合法的
发现自己自作聪明,把第一问中合法的操作存起来当做第二问的答案,然后闷声改了过来
但并没有修改上述bug
结果
这里写图片描述
这个结果不奇怪,因为我把数据下下来了,发现自己第一问没错
这更让人感到奇怪,出题人连这个bug都没卡吗?
顺手打开连带数据一起发的std
发现std中有这样一行
这里写图片描述
然后std和我写的做法是一样的
所以这个神题就被我这么水过去了= =
这里写图片描述
还没完……
去问TA爷,TA给出了一个解决办法:如果要删的线段在图中有出度,就打个标记;如果没有出度,就删掉它,并且找指向它的线段,出度-1,然后再如上判断,递归做下去
由于脑抽+对TA爷的仰慕,开始着手搞这个东西
但调了好久才意识到,这个做法也有bug
因为x前驱的前驱,不一定是x的前驱!
也就是std中那句”拓扑不具有传递性“的那句话!
”我终于又想出了一个错误做法“ ——by TA
所以自己思考是很重要的,不要盲信他人,即使是TA
这里写图片描述
正确的题解:传送门
错误的代码:

#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>#include<set>#include<queue>#include<cmath>#include<cstdlib>#define M 100005using namespace std;char *cp=(char *)malloc(20000000); int n,seg,tot[4],cnt;int A[M],B[M],C[M],D[M],first[4][M],deg[4][M],IN[M];double KK[M],BB[M];struct Point{    int data,tp,id;}a[M<<1];struct edge{    int v,next;}e[4][M*3];void add(int tp,int x,int y){    e[tp][++tot[tp]]=(edge){y,first[tp][x]};    first[tp][x]=tot[tp];    ++deg[tp][y];}double dis1(int id){return (seg-BB[id])/KK[id];}//求x double dis2(int id){return KK[id]*seg+BB[id];}//求y bool cmp(Point a,Point b){    if (a.data==b.data) return a.tp>b.tp;    return a.data<b.data;}bool fcmp(double a,double b){    if (fabs(a-b)<=1e-7) return 0;    return a>b;}struct node{    int tp,id;//tp=0->扫描线上下移动,tp=1->扫描线左右移动     bool operator <(const node other)const    {        if (!tp)            return fcmp(dis1(id),dis1(other.id));        else            return fcmp(dis2(id),dis2(other.id));    }};set<node> s;set<node>::iterator it1,it2;queue<int>q;inline int in () {     int x=0,f=0;    for (;*cp<'0'||*cp>'9';cp++) f|=((*cp)=='-');     for (x=0;*cp>='0'&&*cp<='9';cp++)        x=x*10+*cp-48;     return f?-x:x;}void work1()//点按照y排序,扫描线上下移动,set中按照交点的x排序 {    for (int i=1;i<=n;++i)    {        a[i*2-1].data=B[i],a[i*2-1].tp=(B[i]>=D[i]);        a[i*2].data=D[i],a[i*2].tp=(B[i]<D[i]);        a[i*2-1].id=a[i*2].id=i;    }    sort(a+1,a+n*2+1,cmp);    for (int i=1;i<=n<<1;++i)    {        seg=a[i].data;        node tmp=(node){0,a[i].id},t1,t2;         if (a[i].tp)        {            it1=s.lower_bound(tmp);            it2=s.upper_bound(tmp);            if (it1!=s.begin()&&it2!=s.end())                t1=*(--it1),t2=*it2,                add(2,t1.id,t2.id),                add(0,t2.id,t1.id);            s.erase(tmp);        }        else        {            it1=s.lower_bound(tmp);            it2=s.upper_bound(tmp);            if (it2!=s.end())                t2=*it2,                add(2,a[i].id,t2.id),                add(0,t2.id,a[i].id);            if (it1!=s.begin())                t1=*(--it1),                add(2,t1.id,a[i].id),                add(0,a[i].id,t1.id);            s.insert(tmp);        }    }}void work2()//点按照x排序,扫描线左右移动,set中按照交点的y排序{    for (int i=1;i<=n;++i)        a[i*2-1].data=A[i],a[i*2-1].tp=(A[i]>=C[i]),        a[i*2].data=C[i],a[i*2].tp=(A[i]<C[i]),        a[i*2-1].id=a[i*2].id=i;    sort(a+1,a+n*2+1,cmp);    s.clear();    for (int i=1;i<=n<<1;++i)    {        seg=a[i].data;        node tmp=(node){1,a[i].id},t1,t2;         if (a[i].tp)        {            it1=s.lower_bound(tmp);            it2=s.upper_bound(tmp);            if (it1!=s.begin()&&it2!=s.end())                t1=*(--it1),t2=*it2,                add(1,t1.id,t2.id),                add(3,t2.id,t1.id);            s.erase(tmp);        }        else        {            it1=s.lower_bound(tmp);            it2=s.upper_bound(tmp);            if (it2!=s.end())                t2=*it2,                add(1,a[i].id,t2.id),                add(3,t2.id,a[i].id);            if (it1!=s.begin())                t1=*(--it1),                add(1,t1.id,a[i].id),                add(3,a[i].id,t1.id);            s.insert(tmp);        }    }}main(){    fread(cp,1,20000000,stdin);    n=in();    for (int i=1;i<=n;++i)        A[i]=in(),B[i]=in(),C[i]=in(),D[i]=in(),        KK[i]=1.0*(B[i]-D[i])/(A[i]-C[i]),        BB[i]=B[i]-KK[i]*A[i];    work1();    work2();    for (int i=1;i<=n;++i) IN[i]=deg[0][i];    for (int p,q,i=1;i<=n;++i)    {        p=in();q=in();        if (deg[q][p])        {            printf("%d\n",i);            break;        }        for (int tp=0;tp<4;++tp)            for (int j=first[tp][p];j;j=e[tp][j].next)                --deg[tp][e[tp][j].v];    }    for (int i=1;i<=n;++i)        if (!IN[i])            q.push(i);    for (;!q.empty();q.pop())    {        int k=q.front();        printf("%d %d\n",k,0);        for (int i=first[0][k];i;i=e[0][i].next)        {            --IN[e[0][i].v];            if (!IN[e[0][i].v])                q.push(e[0][i].v);        }    }}
0 0
原创粉丝点击