P1884 [USACO12FEB] Overplanting S

纯粹的發表於2024-03-10

原題連結

題解

把覆蓋的區域變成黑色,然後在區域內劃幾條豎線,一定能分成若干個矩形左右拼接而成的圖形

想象一條豎著的線,它的運動軌跡是不連續的,即他會從一個矩形的豎邊跳到另一個矩形的豎邊,每跳一條豎邊都會對藉著豎邊歸屬的矩形的資訊對這條豎邊的啟用塊進行修改
當豎線的絕對位置發生移動時,計算啟用區間產生的面積

所以哪些資訊需要被記錄?
1.每個矩形的左右兩個豎邊
2.矩形對其左右兩豎邊的修改資訊
3.y的區間塊,以計算啟用長度

code

#define ll long long
#include<bits/stdc++.h>
using namespace std;
struct unit
{
    ll x,y1,y2,op;
}segch[4000005];

bool cmp(unit a,unit b)
{
    return a.x<b.x;
}

ll y[4000005]={0};

struct
{
    ll sit,val;
}tree[10000008];

void pushup(ll node)//有點類似於單點修改,區間查詢
{
    tree[node].val=tree[node*2].val+tree[node*2+1].val;
}

void change(ll node,ll down,ll up,ll y1,ll y2,ll op)
{
    if(y1<down||y2>up) return ;

    if(y1>=up&&y2<=down)
    {
        tree[node].sit+=op;//代表區間是否整個被啟用
        if(tree[node].sit)
        {
            tree[node].val=y[up+1]-y[down];
        }
        else
        {
            tree[node].val=0;
            pushup(node);
        }
        return ;
    }

    ll mid=(up+down)>>1;
    change(node*2,down,mid,y1,y2,op);
    change(node*2+1,mid+1,up,y1,y2,op);
    if(!tree[node].sit)pushup(node);//必須要判斷,因為只有沒有被完整啟用的情況下才能啟用子節點,不然完整啟用被覆蓋了
}

inline void read(ll &x) {
    x = 0;
    ll flag = 1;
    char c = getchar();
    while(c < '0' || c > '9'){
        if(c == '-')flag = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9') {
        x = (x << 3) + (x << 1) + (c ^ 48);
        c = getchar();
    }
    x *= flag;
}

inline void write(ll x)
{
    if(x < 0){
        putchar('-');
        x = -x;
    }
    if(x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}

int main()
{
    ll n;
    read(n);
    for(ll i=1;i<=n;i++)
    {
        ll x1,y1,x2,y2;
        read(x1); read(y1); read(x2); read(y2);
        if(x1>x2)swap(x1,x2);
        if(y1<y2)swap(y1,y2);

        segch[2*i-1]={x1,y1,y2,1};//代表需要修改的區間塊,x代表當豎線遍歷到x時他才會發動修改
        segch[2*i]={x2,y1,y2,-1};

        y[2*i-1]=y1;
        y[2*i]=y2;
    }

    n*=2;

    sort(y+1,y+1+n);
    ll len=unique(y+1,y+n+1)-y-1;//這兩步其實都是為了方便拿取每個矩形對其豎邊的修改資訊

    sort(segch+1,segch+1+n,cmp);//這個是為了能夠找出突兀變化豎邊以計算區域面積

    ll ans=0;
    for(ll i=1;i<n;i++)//遍歷所有的修改資訊
    {
        ll y1=lower_bound(y+1,y+len+1,segch[i].y1)-y;
        ll y2=lower_bound(y+1,y+len+1,segch[i].y2)-y;//找出當前修改資訊要修改的區域塊

        change(1,1,len-1,y1-1,y2,segch[i].op);//對當前豎線做區間修改
        if(segch[i+1].x>segch[i].x) ans+=(segch[i+1].x-segch[i].x)*tree[1].val;//tree是實時的豎線啟用區間長度
    }

    write(ans);
    putchar('\n');
    return 0;
}