Luogu P2468 [SDOI2010]粟粟的書架

Luogu P2468 [SDOI2010]粟粟的書架

半道題:前綴和,矩陣
另外半道題:主席數


對於半道題我們開三維數組兩個
sum[i][j][p]的值是以(1,1)爲左上角以(i,j)爲右下角的矩陣中 書的值>=p的和
num[i][j][p]的值是以(1,1)爲左上角以(i,j)爲右下角的矩陣中 書的值>=p的數量
二分查找滿足要求的p

  • ————————————預處理
 for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            int x;
            scanf("%d",&x);
            for(int p=1;p<=1000;p++)
            {
                sum[i][j][p]=sum[i-1][j][p]+sum[i][j-1][p]-sum[i-1][j-1][p];
                num[i][j][p]=num[i-1][j][p]+num[i][j-1][p]-num[i-1][j-1][p]; 

                if(x>=p)
                    sum[i][j][p]+=x,num[i][j][p]+=1;
            }
        }
    }

參考pic1 x是橙色格子的值
[i][j][p]是紅色格子的信息
顯然紅色格子=黃色+綠色+橙色-藍色
pic 1
pic 1 ^

  • ————————————getsum和getnum
int getsum(int x)
{
    return sum[cc][dd][x]-sum[aa-1][dd][x]-sum[cc][bb-1][x]+sum[aa-1][bb-1][x];
}

int getnum(int x)
{
    return num[cc][dd][x]-num[aa-1][dd][x]-num[cc][bb-1][x]+num[aa-1][bb-1][x];
}

參考pic2 以(aa,bb)爲左上角,以(cc,dd)爲右下角的長方形是橙色的
顯然 橙色= 藍色-黃色-綠色+灰色
這裏寫圖片描述
pic2^


對於另外半道題
主席樹維護
和靜態第k小差不多
不過不僅維護個數,還維護和
也是二分查找滿足條件的p
然後主席樹查找>p的書的和和個數


代碼+註釋

#include<cstdio>
#include<cstring>

int sum[210][210][1010],num[210][210][1010];
int aa,bb,cc,dd,goal,n,m,k;

int a[500500],root[500500];
int len=0;
struct nod1{int tot,s,lc,rc;}tr[10001000];

int getsum(int x)
{
    return sum[cc][dd][x]-sum[aa-1][dd][x]-sum[cc][bb-1][x]+sum[aa-1][bb-1][x];
}

int getnum(int x)
{
    return num[cc][dd][x]-num[aa-1][dd][x]-num[cc][bb-1][x]+num[aa-1][bb-1][x];
}
//參考pic2 以(aa,bb)爲左上角,以(cc,dd)爲右下角的長方形是橙色的
//顯然 橙色= 藍色-黃色-綠色+灰色 

int work1()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            int x;
            scanf("%d",&x);
            for(int p=1;p<=1000;p++)
            {
                sum[i][j][p]=sum[i-1][j][p]+sum[i][j-1][p]-sum[i-1][j-1][p];
                num[i][j][p]=num[i-1][j][p]+num[i][j-1][p]-num[i-1][j-1][p]; 

                if(x>=p)
                    sum[i][j][p]+=x,num[i][j][p]+=1;

                //參考pic1 x是橙色格子的值
                //[i][j][p]是紅色格子的信息
                //顯然紅色格子=黃色+綠色+橙色-藍色
            }
        }
    }
    int l,r,ans;
    for(int i=1;i<=k;i++)
    {
        l=1;r=1000;ans=-1;
        scanf("%d %d %d %d %d",&aa,&bb,&cc,&dd,&goal);  
        while(l<=r)
        {
            int mid=(l+r)/2;
            if(getsum(mid)>=goal)
            {
                ans=mid;l=mid+1;
            }
            else
                r=mid-1;
        } 
        if(ans==-1)printf("Poor QLW\n");
        else printf("%d\n",getnum(ans)-((getsum(ans)-goal)/ans));
        //!!!
        //getnum(ans)是大於等於ans的書的數量
        //getsum(asn)是大於等於ans的書加在一起的值
        //可是!大於ans的書是一定取的
        //但值=ans的書不一定全部取,我們要去掉冗餘的 
    } 
}

int work2do(int x,int y,int goal)
{
    int l=1,r=1000,an=0;
    x=root[x-1];y=root[y];
    if(tr[y].tot-tr[x].tot<goal)return -1;
    //全部也沒有這麼多 
    while(l<r)
    {
        int mid=(l+r)/2;
        int g=tr[tr[y].rc].tot-tr[tr[x].rc].tot;
        if(g<=goal)
        {
            //右邊全部值也小於當前想要的goal 
            //那就先取了右邊的吧 
            an+=tr[tr[y].rc].s-tr[tr[x].rc].s;
            //ans加上右邊的個數 
            x=tr[x].lc;y=tr[y].lc;
            //向左邊找 
            goal-=g;
            //更新需要的goal 
            r=mid;
        }
        else
        {
            //什麼都不取先,去左邊找 
            x=tr[x].rc;y=tr[y].rc;
            l=mid+1;
        }
    }
    an+=(goal+l-1)/l;
    //!!!
    //因爲保證了取得到目標值(不然已經return -1回家了)
    //但你可能還沒有取完到這個目標值
    //因爲你到了葉子節點就出來了
    //出來後滿足l==r==你在二分的"quq",
    //即這個區間內大於“quq”的數加起來可以達到目標值 
    //值>“quq”的書我們都需要,可是並不是值等於”quq"的書全部都需要,所以我們出來再考慮
    //值等於"quq" 的書中 取幾本 
    return an;
}

void update(int &rt,int l,int r,int x)
{
    if(rt==0)
    {
        len++;rt=len;
    } 
    tr[rt].s++;
    tr[rt].tot+=x;
    if(l==r)return ;
    int mid=(l+r)/2;
    if(x<=mid)update(tr[rt].lc,l,mid,x);
    else update(tr[rt].rc,mid+1,r,x);
}

int mix(int &x,int y)
{
    if(x==0)
    {
        x=y;return 0;
    }
    if(y==0)return 0;
    tr[x].tot+=tr[y].tot;
    tr[x].s+=tr[y].s;
    mix(tr[x].lc,tr[y].lc);
    mix(tr[x].rc,tr[y].rc);
}

int work2()
{
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&a[i]);
        update(root[i],1,1000,a[i]);
        //每次插入以root[i]爲根一條鏈,這條鏈維護i這個點的信息
        mix(root[i],root[i-1]);
        //將其與前i-1條鏈合併,這樣root[i]爲根的這條鏈維護的就是1~i的信息
    }
    for(int i=1;i<=k;i++)
    {
        scanf("%d %d %d %d %d",&aa,&bb,&cc,&dd,&goal);
        int fff=work2do(bb,dd,goal);
        if(fff==-1)printf("Poor QLW\n");
        else
            printf("%d\n",fff);
    }
}

int main()
{
    scanf("%d %d %d",&n,&m,&k);
    if(n==1)work2();//主席樹 
    else work1();//矩陣 
}

終於AC這題了T-T(是一天還是兩天了。。)

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