扫描线练习

扫描线

题目链接:https://www.acwing.com/problem/content/3071/

大意

给定n个边与x轴、y轴平行的矩形,求面积并

思路

裸题,复习一下。

每个矩形给了左下角的点和右上角的点,记录每个点的横坐标,去重后排序,得到一定条数的扫描线,相邻两条扫描线的距离即区间的长度。

处理每个区间的有限宽度(各个矩形的宽度之和)和长度相乘累加即可。

代码复杂度 O( n 2 l o g n n^2logn n2logn

CODE

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<ll,ll>
const int N=1e3+7;
int n;
struct node
{
    ll x1,y1,x2,y2;
}a[N];
vector<int>vec;
ll solve(int f,int s)
{
    vector<pii>v;
    for(int i=1;i<=n;i++)
    {
        if(a[i].x1<=f&&a[i].x2>=s)
        {
            v.push_back({a[i].y1,a[i].y2});
        }
    }
    if(v.size()==0) return 0;
    ll res=0;
    sort(v.begin(),v.end());
    ll st=v[0].first,ed=v[0].second;
    for(int i=1;i<v.size();i++)
    {
        if(v[i].first<=ed)
            ed=max(v[i].second,ed);
        else
        {
            res+=ed-st;
            st=v[i].first;
            ed=v[i].second;
        }
    }
    res+=ed-st;
    return res*(s-f);

}
int main()
{
    ios::sync_with_stdio(0);
	cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i].x1>>a[i].y1>>a[i].x2>>a[i].y2;
        vec.push_back(a[i].x1);
        vec.push_back(a[i].x2);
    }
    sort(vec.begin(),vec.end());
    vec.erase(unique(vec.begin(),vec.end()),vec.end());
    int si=vec.size();
    ll ans=0;
    for(int i=0;i<si-1;i++)
    {
        ans+=solve(vec[i],vec[i+1]);
    }
    cout<<ans<<endl;
    return 0;
}

三角形面积并

题目链接:https://www.acwing.com/problem/content/2803/

大意

给出n个三角形,求它们的面积并。保留两位小数

思路

处理出三角形的三个顶点和所有三角形相交的点,记录每个点的横坐标。

可以知道,每两条扫描线之间的图形都可以视作为梯形,通过梯形的面积公式(上底+下底)*高/2,我们只需要将当前两条扫描线上的长度之和乘以两扫描线距离/2即可,另注意特判三角形边在扫描线上的情况。

复杂度 O( n 3 l o g n n^3logn n3logn) 多出来的n为处理每两条线段的交点

CODE

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<ll,ll>
#define pdd pair<double,double>
const int N=1e2+7;
const double eps=1e-8,INF=1e6;
int n;
pdd p[N][3];
pdd pp[N];
vector<double>vec;
int sign(double x)
{
    if(fabs(x)<eps) return 0;
    return x>0?1:-1;
}
int dcmp(double x,double y)
{
    if(fabs(x-y)<eps)   return 0;
    return x>y?1:-1;
}
pdd operator -(pdd x,pdd y)
{
    return {x.first-y.first,x.second-y.second};
} 
pdd operator +(pdd x,pdd y)
{
    return {x.first+y.first,x.second+y.second};
} 
pdd operator *(pdd x,double y)
{
    return {x.first*y,x.second*y};
}
double operator *(pdd x,pdd y)
{
    return x.first*y.second-x.second*y.first;
}
double operator &(pdd x,pdd y)
{
    return x.first*y.first+x.second*y.second;
}
bool on_line(pdd x,pdd y,pdd z)
{
    return sign((x-y)&(x-z))<=0;
}
pdd GetLineIntersection(pdd u,pdd x,pdd v,pdd y) 
{
    if(!sign(x*y))  return {INF,INF};
    auto t=u-v;
    auto o=y*t/(x*y);
    auto oo=u+x*o;
    if(on_line(oo,u,u+x)&&on_line(oo,v,v+y))
        return oo;
    return {INF,INF};
}
double solve1(double x,int tot)
{
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        if(dcmp(p[i][0].first,x)>0||dcmp(p[i][2].first,x)<0)
            continue;
        if(!dcmp(p[i][0].first,x)&&!dcmp(p[i][1].first,x))
        {
            if(tot)
                pp[cnt++]={p[i][0].second,p[i][1].second};
        }
        else if(!dcmp(p[i][2].first,x)&&!dcmp(p[i][1].first,x))
        {
            if(!tot)
                pp[cnt++]={p[i][2].second,p[i][1].second};
        }
        else
        {
            double d[3];
            int u=0;
            for(int j=0;j<3;j++)
            {
                auto v=GetLineIntersection(p[i][j],p[i][(j+1)%3]-p[i][j],{x,-INF},{0,INF*2});
                if(dcmp(v.first,INF))
                    d[u++]=v.second;
            }
            if(u)
            {
                sort(d,d+u);
                pp[cnt++]={d[0],d[u-1]};
            }
        }
    }
    if(!cnt)    return 0;
    for(int i=0;i<cnt;i++)
    {
        if(pp[i].first>pp[i].second)
            swap(pp[i].first,pp[i].second);
    }
    sort(pp,pp+cnt);
    double res = 0, st = pp[0].first, ed = pp[0].second;
    for (int i = 1; i < cnt; i ++ )
        if (pp[i].first <= ed) ed = max(ed, pp[i].second);
        else
        {
            res += ed - st;
            st = pp[i].first, ed = pp[i].second;
        }
    res += ed - st;
    return res;
}
double solve(double x,double y)
{
    return (solve1(x,1)+solve1(y,0))*(y-x)/2.0;
}
int main()
{
    //ios::sync_with_stdio(0);
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>p[i][0].first>>p[i][0].second>>p[i][1].first>>p[i][1].second>>p[i][2].first>>p[i][2].second;
        vec.push_back(p[i][0].first);
        vec.push_back(p[i][1].first);
        vec.push_back(p[i][2].first);
        sort(p[i],p[i]+3);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)
        {
            for(int k=0;k<3;k++)
            {
                for(int l=0;l<3;l++)
                {
                    auto x=GetLineIntersection(p[i][k],p[i][(k+1)%3]-p[i][k],p[j][l],p[j][(l+1)%3]-p[j][l]);
                    if(dcmp(x.first,INF))
                    {
                        vec.push_back(x.first);
                    }
                }
            }
        }
    }
    sort(vec.begin(),vec.end());
    double res=0;
    for(int i=0;i+1<vec.size();i++)
    {
        if(dcmp(vec[i],vec[i+1]))
            res+=solve(vec[i],vec[i+1]);
    }
    printf("%.2lf\n",res);
    return 0;
}

Hopping Rabbit——2021牛客暑期多校训练营6 H

题目链接:https://ac.nowcoder.com/acm/contest/11257/H

大意

给定n个矩阵位置,询问是否存在点(x,y)满足所有点(x+kd,y+kd)(k∈Z,d已知)不在矩阵内。

思路

将n个矩阵全部在一个d*d的矩阵中表示出来(相对位置),用扫描线+线段树寻找未被矩阵覆盖的点。

线段树维护的是每一列该区间的最小覆盖矩阵数量,每次更换列数时更新线段树(通过记录矩阵左右边后就可以直接更新了,遍历到左边加,否则右边减),如果根节点为0,线段树向下寻找为0的叶子节点输出。

比赛时出了思路倒在了不知道怎么在d*d的矩阵中找合法点,有过扫描线的想法但只写过一次矩形面积并题不会其他用法,太可惜了!比赛快结束时还把思路和其他队的几何选手讨论了一下也没想到做法,几个水货。。。

CODE

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+7;
#define lch (k*2)
#define rch (k*2+1)
#define mid ((l+r)/2)
struct node
{
    int l,r,op;
};
vector<node>vec[N];
int tr[N*4],lazy[N*4];
void pushdown(int k)
{
    if(!lazy[k])    return ;
    lazy[lch]+=lazy[k];
    lazy[rch]+=lazy[k];
    tr[lch]+=lazy[k];
    tr[rch]+=lazy[k];
    lazy[k]=0;
}
void update(int k,int l,int r,int ql,int qr,int val)
{
    if(ql<=l&&r<=qr)
    {
        lazy[k]+=val;
        tr[k]+=val;
        return ;
    }
    pushdown(k);
    if(ql<=mid)
    {
        update(lch,l,mid,ql,qr,val);
    }
    if(mid+1<=qr)
    {
        update(rch,mid+1,r,ql,qr,val);
    }
    tr[k]=min(tr[lch],tr[rch]);
}
int qry(int k,int l,int r)
{
    if(l>=r)
    {
        return l;
    }
    pushdown(k);
    if(tr[lch]==0)
    {
        return qry(lch,l,mid);
    }
    else
    {
        return qry(rch,mid+1,r);
    }
}
void add(int x1,int x2,int y1,int y2)
{
    vec[x1].push_back({y1,y2,1});
    //cout<<x1<<endl;
    //cout<<vec[x1][0].l<<' '<<vec[x1][0].r<<' '<<vec[x1][0].op<<endl;
    vec[x2+1].push_back({y1,y2,-1});
    //cout<<x2<<endl;
    //cout<<vec[x2+1][0].l<<' '<<vec[x2+1][0].r<<' '<<vec[x2+1][0].op<<endl;
}
int main()
{
    ios::sync_with_stdio(0);
    int n,d;
    cin>>n>>d;
    for(int i=1;i<=n;i++)
    {
        int x1,x2,y1,y2;
        cin>>x1>>y1>>x2>>y2;
        x2--;y2--;
        if(y2-y1+1>=d)
        {
            y1=0;
            y2=d-1;
        }
        if(x2-x1+1>=d)
        {
            x1=0;
            x2=d-1;
        }
        x1=(x1%d+d)%d;
        x2=(x2%d+d)%d;
        y1=(y1%d+d)%d;
        y2=(y2%d+d)%d;
        if(x2>=x1)
        {
            if(y2>=y1)
            {
                add(x1,x2,y1,y2);
            }
            else
            {
                add(x1,x2,y1,d-1);
                add(x1,x2,0,y2);
            }
        }
        else
        {
            if(y2>=y1)
            {
                add(x1,d-1,y1,y2);
                add(0,x2,y1,y2);
            }
            else
            {
                add(x1,d-1,y1,d-1);
                add(x1,d-1,0,y2);
                add(0,x2,y1,d-1);
                add(0,x2,0,y2);
            }
        }
    }
    for(int i=0;i<d;i++)
    {
        for(auto it:vec[i])
        {
            update(1,0,d-1,it.l,it.r,it.op);
        }
        if(tr[1]==0)
        {
            cout<<"YES"<<endl;
            cout<<i<<' '<<qry(1,0,d-1)<<endl;
            return 0;
        }
    }
    cout<<"NO"<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

第十页

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值