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(是一天还是两天了。。)

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