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

传送门
思路:
神题
总结——写了一个错误做法,然后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 100005
using 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);
        }
    }
}
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值