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 ^
- ————————————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(是一天還是兩天了。。)