[SDOI2010]粟粟的書架
阿新 • • 發佈:2018-01-04
i+1 turn 新建 旅行 logs 歷史 sdoi2014 至少 post
直接開兩個前綴和:
\(data[d][i][j]\)為值到了d時的權值和,\(sum[d][i][j]\)為值到了d時的人數和。
那麽每次二分一個權值d,然後二維前綴和查詢Check即可。
題目大意:
網址:https://daniu.luogu.org/problemnew/show/2468
大意:本題有兩問:
[1] 給定一個\(R*C\)的帶權矩陣,詢問\(2×10^5\)次在一個子矩陣內至少選擇多少個點使他們的權值和大於\(H\)。
[2] 給定長度為L的一條鏈,詢問\(2×10^4\)次在一個區間\([L,R]\)中至少選擇多少個點使他們的權值和大於\(H\)。
數據範圍:對於兩問,都有權值\(P<=1000\),第一問中\(R、C<=200\),第二問中\(L<=5\times 10^5\)。
題目解法
第一問(Solve1):
觀察到\(R×C×P\)無論是在時間還是在空間上都是可以接受的。
\(data[d][i][j]\)為值到了d時的權值和,\(sum[d][i][j]\)為值到了d時的人數和。
那麽每次二分一個權值d,然後二維前綴和查詢Check即可。
第二問(Solve2):
正解是主席樹,然而我自己 yy 出了一個 .... 總統樹 ?
還是線段樹為基礎,進行可持久化,從大往小對於每一個權值建立一棵樹。
直接建1000棵線段樹肯定是不現實的。所以考慮用主席樹的那種搞法。
對於更新路徑上的節點。如果還沒有新建節點,就建立一個新結點。
那麽向下走的時候,走的那個方向如果還沒有建立新結點,則要新建,否則走本輪建的結點。
不走的那個方向直接用前一歷史版本的即可。
if(ps<=mid){
if(!rs[ro])rs[ro] = rs[lst]; //沿用上一版本
if(ls[ro] == ls[lst])ls[ro] = 0; //更新的路徑上所有節點必須新建。
Update2(ls[ro],l,mid,ps,dt,ls[lst]);
}
上面這個代碼中lst是上一個歷史版本,ro為本輪版本。
與這題類似的還有[SDOI2014]旅行,都是要自己 yy 可持久化或動態的數據結構。
完成建樹後,一樣的對與每一個詢問二分一個權值d,然後Check即可。
具體實現代碼:
#include<bits/stdc++.h> #define maxn 10000000 #define gi(x) scanf("%d",&x); using namespace std; int oo,R,C,M,H,x11,x22,y11,y22; int data[1005][205][205],sum[1005][205][205]; int ls[maxn],rs[maxn],rt[1005],val[maxn],cnt[maxn]; vector<int>g[1005]; int Calc(int hg,int od){ if(od == 0)return data[hg][x22][y22] + data[hg][x11-1][y11-1] - data[hg][x22][y11-1] - data[hg][x11-1][y22]; if(od == 1)return sum[hg][x22][y22] + sum[hg][x11-1][y11-1] - sum[hg][x22][y11-1] - sum[hg][x11-1][y22]; } int GetAns1(){ if(Calc(1,0) < H)return -1; int L = 1,R = 1000,Res=-1,Ans=0,Last = 0; while(L<=R){ int mid = (L+R)>>1; if(Calc(mid,0) >= H){Res = mid; L =mid+1;} else R = mid-1; } Ans = Calc(Res+1,1),Last = Calc(Res+1,0); return Ans + (H-Last+(Res-1))/Res; } void Solve1(){ for(int i = 1,d; i <= R; i ++) for(int j = 1; j <= C; j ++){ gi(d); data[d][i][j] = d; sum[d][i][j] = 1; } for(int d = 1; d <= 1000; d ++) for(int i = 1; i <= R; i ++) for(int j = 1; j <= C; j ++) data[d][i][j] += data[d][i][j-1] + data[d][i-1][j] - data[d][i-1][j-1], sum[d][i][j] += sum[d][i][j-1] + sum[d][i-1][j] - sum[d][i-1][j-1]; for(int d = 999; d >= 1; d --) for(int i = 1; i <= R; i ++) for(int j = 1; j <= C; j ++) data[d][i][j] += data[d+1][i][j], sum[d][i][j] += sum[d+1][i][j]; for(int sq = 1,f; sq <= M; sq ++){ gi(x11); gi(y11); gi(x22); gi(y22); gi(H); f = GetAns1(); if(f == -1)printf("Poor QLW\n"); else printf("%d\n",f); } return; } void Build2(int &ro,int l,int r){ ro = ++oo; if(l == r)return; int mid = (l+r)>>1; Build2(ls[ro],l,mid); Build2(rs[ro],mid+1,r); } void PushUp2(int ro){ val[ro] = val[ls[ro]] + val[rs[ro]]; cnt[ro] = cnt[ls[ro]] + cnt[rs[ro]]; } int Ret2(int ro,int typ){return (!typ) ? val[ro] : cnt[ro];} int Query2(int ro,int l,int r,int tl,int tr,int typ){ if(!ro || r<tl || l>tr)return 0; if(tl<=l && r<=tr)return Ret2(ro,typ); int mid = (l+r)>>1; return Query2(ls[ro],l,mid,tl,tr,typ)+Query2(rs[ro],mid+1,r,tl,tr,typ); } void Update2(int &ro,int l,int r,int ps,int dt,int lst){ if(!ro){ ro = ++oo; val[ro] = val[lst]; cnt[ro] = cnt[lst]; } if(l == r){cnt[ro]+=1; val[ro]+=dt; return;} int mid = (l+r)>>1; if(ps<=mid){ if(!rs[ro])rs[ro] = rs[lst]; if(ls[ro] == ls[lst])ls[ro] = 0; Update2(ls[ro],l,mid,ps,dt,ls[lst]); } else if(ps>mid){ if(!ls[ro])ls[ro] = ls[lst]; if(rs[ro] == rs[lst])rs[ro] = 0; Update2(rs[ro],mid+1,r,ps,dt,rs[lst]); } PushUp2(ro); } void Solve2(){ for(int i = 1,d; i <= C; i ++) {gi(d); g[d].push_back(i);} Build2(rt[1001],1,C); for(int i = 1000; i >= 1; i --){ if(g[i].empty())rt[i] = rt[i+1]; for(int j = 0; j < g[i].size(); j ++) Update2(rt[i],1,C,g[i][j],i,rt[i+1]); } for(int sq = 1; sq <= M; sq ++){ gi(x11); gi(y11); gi(x22); gi(y22); gi(H); if(Query2(rt[1],1,C,y11,y22,0) < H){printf("Poor QLW\n");continue;} int L = 1,R = 1000,Res = -1,Ans,Last; while(L<=R){ int mid = (L+R)>>1; if(Query2(rt[mid],1,C,y11,y22,0) >= H){Res = mid; L = mid+1;} else R = mid - 1; } Ans = Query2(rt[Res+1],1,C,y11,y22,1),Last = Query2(rt[Res+1],1,C,y11,y22,0); printf("%d\n",Ans + (H-Last+(Res-1))/Res); } return; } int main(){ gi(R); gi(C); gi(M); if(R!=1)Solve1(); if(R == 1)Solve2(); return 0; }
[SDOI2010]粟粟的書架