1. 程式人生 > >LOJ #6299. 「CodePlus 2018 3 月賽」白金元首與克勞德斯

LOJ #6299. 「CodePlus 2018 3 月賽」白金元首與克勞德斯

題目描述

千里白金雪滿天 烽火江山起狼煙 分手竟兵刃相見

1941.7.

蘇聯軍隊出乎意料的反抗力量、前線德軍的補給困難 —— 元首 Adolf 望著天空的雲層陷入沉思……

在 xyxyxy-直角座標平面的天空中,有 nnn 片四邊平行於座標軸的矩形雲朵。每一片雲由一個五元組 (xi,yi,wi,hi,di)(x_i, y_i, w_i, h_i, d_i)(xi​,yi​,wi​,hi​,di​) 表示,其中 (xi,yi)(x_i, y_i)(xi​,yi​) 為雲左下角頂點的座標,wiw_iwi​ 表示雲在 xxx 軸方向的寬度,hih_ihi​ 表示雲在 yyy 軸方向的長度,di∈{0,1}d_i \in \{0, 1\}di​∈{0,1} 為雲的移動方向(000 為橫向,111 為縱向)。具體來說,滿足 di=0d_i = 0di​=0 的雲沿 xxx 軸正方向以每秒 111 長度單位的速率不斷移動,而滿足 di=1d_i = 1di​=1 的雲沿 yyy 軸正方向以每秒 111 長度單位的速率不斷移動。

元首發現,所有的雲在此時沒有重疊的面積。他將這個時刻記作時刻 000。他想知道,對於 (−∞,+∞)(-\infty, +\infty)(−∞,+∞) 中的任意時刻和平面上的任意一個點,最多可以同時被多少片雲覆蓋。一個點在某時刻被一朵雲覆蓋當且僅當這個點位於該時刻雲朵所處矩形的內部(不含邊界)

你需要編寫程式幫助元首滿足他的好奇心。

輸入格式

從標準輸入讀入資料。

輸入的第一行包含一個正整數 TTT —— 資料的組數。接下來包含 TTT 組資料,格式如下,資料間沒有空行。

  • 第 111 行:一個正整數 nnn —— 雲朵的數量。
  • 接下來 nnn 行:每行五個空格分隔的整數 xix_ixi​、yiy_iyi​、wiw_iwi​、hih_ihi​ 和 did_idi​ —— 描述一朵雲在時刻 000 的狀態。

輸出格式

輸出到標準輸出。

對於每組資料輸出一行 —— 在任意時刻,覆蓋平面上任意一個點的雲朵數目的最大值。

樣例

樣例輸入

3
1
0 0 1 1 0
3
0 -10 10 10 1
10 0 10 10 1
-10 0 10 10 0
3
0 10 10 10 1
10 20 10 10 1
10 0 10 10 0

樣例輸出

1
2
2

第 111 組資料中,任意時刻的任意一個點至多被惟一的一片雲覆蓋。

第 222 組資料中,下圖從左至右分別示意時刻 000、時刻 444、時刻 111111 的情形。

Sample

第 333 組資料中,時刻 000 對應第 222 組資料時刻 202020 的情形。在該組資料中,(−20,0)(-20, 0)(−20,0) 內的時刻均有 222 片雲覆蓋同一個點。請注意考察範圍 (−∞,+∞)(-\infty, +\infty)(−∞,+∞) 包含時刻 000 之前的時間段。

資料範圍與提示

子任務

對於所有資料,有 1≤T≤151 \leq T \leq 151≤T≤15,−5×108≤xi,yi≤−5×108-5 \times 10^8 \leq x_i, y_i \leq -5 \times 10^8−5×108≤xi​,yi​≤−5×108,1≤wi,hi≤−5×1081 \leq w_i, h_i \leq -5 \times 10^81≤wi​,hi​≤−5×108,di∈{0,1}d_i \in \{0, 1\}di​∈{0,1}。

測試點編號 nnn 特殊約定
1 ≤1\leq 1≤1
2 ≤2\leq 2≤2
3 ≤10\leq 10≤10 −50≤xi,yi≤50-50 \leq x_i, y_i \leq 50−50≤xi​,yi​≤50,1≤wi,hi≤501 \leq w_i, h_i \leq 501≤wi​,hi​≤50
4 ≤50\leq 50≤50
5 ≤100\leq 100≤100 wi=hi=1w_i = h_i = 1wi​=hi​=1
6 ≤1000\leq 1000≤1000 −103≤xi,yi≤103-10^{3} \leq x_i, y_i \leq 10^{3}−103≤xi​,yi​≤103,1≤wi,hi≤1031 \leq w_i, h_i \leq 10^{3}1≤wi​,hi​≤103
7
8 wi=hi=1w_i = h_i = 1wi​=hi​=1
9 ≤100000\leq 100000≤100000
10

Dann soll die Armee eben kehrt machen!

題面與史實無關。

提示

最大輸入約為 50 MiB,請注意程式在讀入上的耗時哦。

來自 CodePlus 2018 3 月賽,清華大學電腦科學與技術系學生演算法與競賽協會 榮譽出品。 Credit:idea/呂時清 命題/呂時清 驗題/丁子鈞 Git Repo:https://git.thusaac.org/publish/CodePlus3 感謝騰訊公司對此次比賽的支援。

看穿題目

有很多可以橫向或者縱向移動的矩陣,

問是否有點在某一時刻被兩個矩陣覆蓋

因為開始沒有矩陣相交,而每個矩陣只可以橫向或縱向以同一速度移動

也就是說相同方向的矩陣是相對靜止(不得不佩服自己科學還記得)

所以一個點最多被不同方向的矩陣覆蓋兩邊

只要滿足在某一時刻,一個縱向移動的矩形四個角的任意一個角(x3,y3)再一個橫向矩陣內部

x1+t<x3<x2+t      1式

y1<t+y3<y2         2式

1式加2式:x1+y1<x3+y3<x2+y2

排序去重後二分(找最後一個小於和第一個大於答案,最後一個小於的位置大於等於第一個大於就為2)即可

細節較多

#include<cstdio>
#include<algorithm>
using namespace std;
inline int read()
{
    int ret=0; bool f=0;
    char ch=getchar();
    while(ch<'0'||ch>'9') 
    {
        if(ch=='-') f=1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') 
        ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();
    return f?-ret:ret;
}
 
int n,cnt1,cnt2,num,t1,t2;
const int N=1e5+5;
struct NA{
    int l,r;
}a[N];
 
bool cmp(NA  i,NA j)
{
    return i.l<j.l||i.l==j.l&&i.r>j.r;
}
 
struct NB{
    int s1,s2,s3,s4;
}q[N];
 
int find1(int l,int r,int k)
{
    int ans=0;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(a[mid].l<k) ans=mid,l=mid+1;
            else r=mid-1;
    }
    return ans;
}
 
int find2(int l,int r,int k)
{
    int ans=r+1;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(a[mid].r>k) ans=mid,r=mid-1;
            else l=mid+1;
    }
    return ans;
}
 
int main()
{
    int T=read();
    while(T--)
    {
        n=read(); cnt1=cnt2=0;
        for(int i=1;i<=n;i++) 
        {
            int x1=read(),y1=read(),
                x2=x1+read(),y2=y1+read();
            int t=read();
            if(!t) 
                a[++cnt1].l=x1+y1,
                a[cnt1].r=x2+y2;
                else q[++cnt2].s1=x1+y1,
                     q[cnt2].s2=x2+y1,
                     q[cnt2].s3=x1+y2,
                     q[cnt2].s4=x2+y2;
        }
        sort(a+1,a+cnt1+1,cmp);
        //for(int i=1;i<=cnt2;i++)
       	//printf("%d %d %d %d\n",q[i].s1,q[i].s2,q[i].s3,q[i].s4);
        //printf("\n");
        num=1;
        for(int i=2;i<=cnt1;i++)
            if(a[i].l>a[i-1].l&&a[i].r>a[i-1].r) 
                a[++num].l=a[i].l,a[num].r=a[i].r;
        //for(int i=1;i<=num;i++)
        //	printf("%d %d\n",a[i].l,a[i].r);
        bool f=0;
        for(int i=1;i<=cnt2;i++)
        {
            t1=find1(1,num,q[i].s1),
            t2=find2(1,num,q[i].s1);
            if(t1>=t2) 
            {
                puts("2"); 
			//	printf("%d %d %d %d\n",i,t1,t2,1); 
				f=1; break;
            } 
            t1=find1(1,num,q[i].s2),
            t2=find2(1,num,q[i].s2);
            if(t1>=t2) 
            {
                puts("2"); 
			//	printf("%d %d %d %d\n",i,t1,t2,2);
				f=1; break;
            } 
            t1=find1(1,num,q[i].s3),
            t2=find2(1,num,q[i].s3);
            if(t1>=t2) 
            {
                puts("2"); 
			//	printf("%d %d %d %d\n",i,t1,t2,3);
				f=1; break;
            } 
            t1=find1(1,num,q[i].s4),
            t2=find2(1,num,q[i].s4);
            if(t1>=t2) 
            {
                puts("2"); 
			//	printf("%d %d %d %d\n",i,t1,t2,4);
				f=1; break;
            } 
        }
        if(!f) 
            if(n) puts("1");
                else puts("0");
    } 
    return 0;
}