【cqbzoj1526】 分梨子 亂搞(不是dp) 解題報告 c++

分梨子

時間限制: 1 Sec 內存限制: 64 MB

題目描述

  Finley家的院子裏有棵梨樹,最近收穫了許多梨子。於是,Finley決定挑出一些梨子,分給幼稚園的寶寶們。可是梨子大小味道都不太一樣,一定要儘量挑選那些差不多的梨子分給孩子們,那些分到小梨子的寶寶纔不會哭鬧。   每個梨子都具有兩個屬性值,Ai和Bi,本別表示梨子的大小和甜度情況。假設在選出的梨子中,兩個屬性的最小值分別是A0和B0。只要對於所有被選出的梨子i,都滿足C1*(Ai-A0)+C2*(Bi-B0)≤C3(其中,C1、C2和C3都是已知的常數),就可以認爲這些梨子是相差不多的,可以用來分給小朋友們。   那麼,作爲幼稚園園長的你,能算出最多可以挑選出多少個梨子嗎?
輸入
第一行一個整數N(1≤N≤2000),表示梨子的總個數。 第二行三個正整數,依次爲C1,C2和C3(C1,C2≤2000,C3≤10^9)。 接下來的N行,每行兩個整數。第i行的兩個整數依次爲Ai和Bi。

輸出

只有一個整數,表示最多可以選出的梨子個數。

樣例輸入

3
2 3 6
3 2
1 1
2 1

樣例輸出

2

解題報告:

這不是一道dp題

對此題的第一想法,n^3暴力
但是很明顯這會超時
接下來,你要擴寬你的思維,開始解題(亂搞
注意到題目中的

對於所有被選出的梨子i,都滿足C1(AiA0)+C2(BiB0)C3

如果在這裏把A看做平面直角座標系的x軸,B看做y軸,
每一個梨子看做平面直角座標系中的一個點
設Ai-A0=x, Bi-B0=y
極限時不等式左右兩邊相等
那麼有C1x+C2y=C3
推導可得

y=C1C2x+C3C2

它相當於座標系中的一條直線
可以看出,能與梨子(A0,B0)組成一組的梨子不能在這條直線上方
同時,由於題目中定義A0爲選中一組梨子A的最小值,B0爲B的最小值
所以任意一個選中的梨子只能在直線x=A0的右方和直線y=B0的上方
這些直線圍城了一個三角形:
三角形如圖所示
只有在這個三角形內(或邊上)的梨子才能被選中

三角形的三點互相相對位移是一定的
如果O(n^2)枚舉三角形的左下角定點(A0,B0)
(設i點有着組內最小A,j點有着組內最小B,枚舉i,j爲O(n^2))
那麼問題變成了:如何快速求出三角形內的點的數量?

首先見下圖:
這裏寫圖片描述
S陰影=S總-S1-S2+S3
其中S總表示塗了顏色的面積(請忽略圖中的S4)。
1.S總
如果要快速求出S總,那麼在順序枚舉(A0,B0)時要保證三角形的斜邊一次比一次靠左/靠下。最初時,所有點都會在S陰影中,每一次移動直線時只需從S中以O(k)時間刪去k個移動後處於白色區域的點就可以了
具體做法,用O(n^2)時間複雜度找出所有合法(A0,B0)組合(當i有着A0,j有着B0時Ai小於Aj且Bj小於Bi),存入一個數組內,稱爲虛擬點
對該數組按照每個虛擬點代表的向量在向量(C1,C2)上的投影模長從大到小排序,接下來順序枚舉,就可以保證直線的移動順序(此處不多解釋)。
把所有的代表梨子的點也按照以上規則進行排序,壓入隊列,每次移動直線時只需從隊頭連續刪除k個節點就能保證剩下節點全部在S總之內了。

2.S1和S2
注意到題目中Ai,Bi<=2000,於是使用兩個大小爲2000的樹狀數組維護前綴和,每次從S總隊列刪除節點時把節點也從樹狀數組中刪去即可,
查詢,修改均爲logL

3.S3
S3並不需要動態維護(不多解釋)
設f[i][j]爲在矩形(1,1)-(i,j)中的代表梨子的節點數量
顯然有f(i,j)=f(i1,j)+f(i,j1)f(i1,j1)+pear(i,j)
實現時把(i,j)有梨子的f[i][j]=1
然後f[i][j]+=f[i-1][j]+f[i][j-1]-f[i-1][j-1]

最後取S陰影的最大值輸出即可得到答案
(腦洞夠大啊= =)

AC代碼

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
const int EPS=5000;
const int MAXN=2100;
const int MAXL=2010;

inline int max(const int &a,const int &b)
{return a>b?a:b;}

int pear[MAXN][2];
double stdx,stdy,sq;
struct mpoint{
    int x,y;
    double sh;
    inline void cal(){
        sh=((stdx*x+stdy*y)/sq)*EPS;
    }
}points[(MAXN-400)*(MAXN-400)],pv[MAXL];
bool operator<(const mpoint&a,const mpoint&b){
    return a.sh>b.sh;
}
int pt;

int cx[MAXL],cy[MAXL];
int atm;
inline int lowbit(const int &a)
{return a&-a;}
inline int query(int *c,int p){
    int ret=0;
    while(p){
        ret+=c[p];
        p-=lowbit(p);
    }
    return ret;
}
inline void add(int *c,int p,const int &v){
    while(p<=atm){
        c[p]+=v;
        p+=lowbit(p);
    }
}

int rect[MAXL][MAXL];

int main(){
    int n;
    scanf("%d",&n);
    atm=n;
    int c1,c2,c3;
    int mxa=0,mxb=0;
    scanf("%d%d%d",&c1,&c2,&c3);
    stdx=c1;
    stdy=c2;
    sq=sqrt(c1*c1+c2*c2);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&pear[i][0],&pear[i][1]);
        mxa=max(mxa,pear[i][0]);
        mxb=max(mxb,pear[i][1]);
        rect[pear[i][0]][pear[i][1]]++;
        pv[i].x=pear[i][0];
        pv[i].y=pear[i][1];
        pv[i].cal();
    }
    for(int i=1;i<=mxa;i++)//初始化rect
        for(int j=1;j<=mxb;j++){
            rect[i][j]+=
            rect[i-1][j]+rect[i][j-1]
            -rect[i-1][j-1];
    }
    for(int i=1;i<=n;i++)//初始化可能的向量
        for(int j=1;j<=n;j++){
            if(pear[i][1]<pear[j][1]||
               pear[j][0]<pear[i][0])//這兩個點不兼容
                continue;
            points[++pt].x=pear[i][0];
            points[pt].y=pear[j][1];
            points[pt].cal();
        }

    std::sort(points+1,points+(pt+1));//按在y=x投影的模長排序
    std::sort(pv+1,pv+(n+1));

    for(int i=1;i<=n;i++){//初始化樹狀數組
        add(cx,pv[i].x,1);
        add(cy,pv[i].y,1);
    }
    double lk=-(double)c1/c2;
    double lb=(double)c3/c2;
    int ans=0,up=1;
    for(int i=1;i<=pt;i++){
        while((lk*(pv[up].x-points[i].x)+lb)
              <(pv[up].y-points[i].y)
              ){
            add(cx,pv[up].x,-1);
            add(cy,pv[up].y,-1);
            up++;
        }
        ans=max(ans,n-up+1//總點數
                -query(cx,points[i].x-1)//左邊
                -query(cy,points[i].y-1)//下面
                +rect[points[i].x-1]
                [points[i].y-1]);//補上一個矩形
    }
    printf("%d\n",ans);
}

接下來是廢話
更神奇的是這題被分到了dp作業裏,
於是一直猛想轉移方程,(當然想不出來= =)
也搜不到題解
無奈之下
在知道上提問
結果遇見大神(雖然不全對,但是幫忙打開了腦洞)
這裏寫圖片描述
然後開始亂搞實現,解決各種問題
然後成功成爲第一個學校oj上ac的人
寫博客希望能給其他搜不到題解的人一點頭緒
亂搞出奇跡系列

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章