毯子 題解(COCI 2008-2009Final C)

[題意]

N 塊矩形毯子鋪在地上。0秒時(0,0)處有一桶油倒了,然後開始流呀流,每秒往八個方向擴散一個單位。注意,這裏的座標描述一個單元格,不表示點。M個詢問,每次問一個時間點被油染到的毯子面積(若有毯子重疊,面積也要累加,如一個單位格被三個毯子覆蓋,那麼被油染到之後就算3個單位面積)。

[思路]

50分:暴力!n*m枚舉每個詢問時每塊毯子被染到的範圍.直接把頂點算出.

100分:

     有兩個切入點:

1)每塊毯子被染的區域增量隨時間改變是有規律的:

     可以分成兩個部分:

第一部分是一個公差爲2的等差數列:如圖所示,增量分別是3,5,7塊;

第二部分是一個常數列:圖中的常數爲3.

每個部分都是一個獨立的區間.

2)每塊毯子染到的面積是獨立的,可以疊加.

確定了以上的兩點, 就可以聯想到刷漆.把每個時間點的狀態疊加.

每個時間點的狀態由兩個變量來表示:b,k.b表示當前時間點的染到的面積增量的值,k表示當前時間點染到的面積增量的公差.

對於每個時間點t對t-1的增量就可以表示爲:b+k+b[t](b[t],表示t時間點的面積增量)

優化:

因爲油的變化趨勢是隨原點對稱的,所以把所有矩形都翻到右上角去,

可以在刷漆時更簡便.

注意:

1. 輸入數據有負數,慎用讀入掛

2. 在翻折矩形之後,矩形的個數增加了,數組比忘了開大點!!

 

[啓發]

1.    對於詢問類的問題可以離線做.

2.   隨時間增長的變量可以找規律,挖性質.

<span style="font-family:Comic Sans MS;font-size:18px;">#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#define ll long long
using namespace std;
inline void rd(int &res){
    res=0;
    int k=1;char c;
    while(c=getchar(),c<48&&c!='-');
    if(c=='-'){k=-1;c=getchar();}
    do{
        res=(res<<1)+(res<<3)+(c^48);
    }while(c=getchar(),c>=48);
    res*=k;
}
inline void print(ll k){
    if(k==0)return ;
    print(k/10);
    putchar((k%10)^48);
}
inline void sc(ll k){
    print(k);
    if(k==0)putchar('0');
    putchar('\n');
}
const int M=100005;
const int N=1e6+5;
struct node{int x1,y1,x2,y2;}A[M*4],B[M*4];
int n,m;
ll res[N];
struct LAY{ll b,k;};
vector<LAY>brush[N];
void add(int t,ll b,ll k){//b表示常量,k表示增量  
    brush[t].push_back((LAY){b,k});
}
void solve(){
    int i,j,k,tot=0,now=n;
    for(i=1;i<=now;i++){//矩形反轉 
        int x1=A[i].x1,x2=A[i].x2,y1=A[i].y1,y2=A[i].y2;
        if(x1>=0&&x2>=0&&y1>=0&&y2>=0){
            B[++tot]=(node){x1,y1,x2,y2};continue;
        }   
        if(x1<0){
            if(x2<0) A[++now]=(node){-x2,y1,-x1,y2};
            else {
                A[++now]=(node){1,y1,-x1,y2};//分成兩塊
                A[++now]=(node){0,y1,x2,y2};
            }
            continue;
        }
        else if(x1>=0&&x2>=0&&y1<0){
            if(y2<0)B[++tot]=(node){x1,-y2,x2,-y1};
            else {
                B[++tot]=(node){x1,0,x2,y2};
                B[++tot]=(node){x1,1,x2,-y1};
            }
        }
    }
    for(i=1;i<=tot;i++){
        int x1=B[i].x1,x2=B[i].x2,y1=B[i].y1,y2=B[i].y2;
        int t1,t2,t3;
        ll s1,s2;
        t1=max(x1,y1);
        t2=min(x2,y2);
        t3=max(x2,y2);//算出兩個部分分別的時間點
        s1=1ll*(min(x2,t1)-max(x1,0)+1)*(min(y2,t1)-max(y1,0)+1);
        if(x2>y2)s2=y2-y1+1;
        else s2=x2-x1+1;
        if(t2<t1)t2=t1;
        add(t1,s1,2);//刷漆:第一部分初始值爲s1,公差爲2
        add(t2+1,-s1-1ll*(t2-t1+1)*2,-2);//結束時把第一部分增加的值減去
        add(t2+1,s2,0);//常量部分
        add(t3+1,-s2,0);
    }
    ll b=0,plus=0,ans=0;
    for(i=0;i<N;i++){
        b+=plus;//公差
        for(j=0;j<brush[i].size();j++){
            b+=brush[i][j].b;plus+=brush[i][j].k;//刷漆累加
        }
        ans+=b;
        res[i]=ans;
    }
}
int main(){
    rd(n);
    int t;
    for(int i=1;i<=n;i++){
        rd(A[i].x1);rd(A[i].y1);
        rd(A[i].x2);rd(A[i].y2);
    }
    solve();
    rd(m);
    for(int i=1;i<=m;i++){
        rd(t);
        sc(res[t]);
    }
    return 0;
}
20160625離線賽/COCI 2008-2009FinalC


發佈了39 篇原創文章 · 獲贊 2 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章