异世界背包
\(T3\) 异世界背包题解
网页链接 : 传送门。
难度 : 提高 \(+\)。
码量 : \(3KB\)。
思路 : 整体二分 \(+\) 二维树状数组 \(+\) 数论分块。
时间复杂度 : \(O((n^2+m)\log\ (n^2+m) \log^2 n+\frac{m}{2}\sqrt{10^{3^3}})\)。
我们假设 \(N\) 为 \(n^2+m\),\(m\) 和 \(n\) 同阶。
时间复杂度 : \(O(N\log\ N \log^2 n+n * 31622)\)。大约是 \(O(10^6\log\ 10^6\log^2\ 10^3+10^3*31622)\),\(2*10^8\)差不多。
声明 : 此题为 《[国家集训队]矩阵乘法》 + 《快速荷叶叶变换》的加强版。
题目大意
由于 \(\mathrm{LZC}\) 最近因为装箱问题而写了超过 \(10^{10}\) 次不同的 \(Dfs\),他决定藐视 \(01\) 背包。\(\mathrm{LZC}\) 想,如果我的物品排成一个矩阵 (\(N*N\)) ,\(\mathrm{LZC}\) 就可以选择某一个子矩阵的第 \(K\) 大的价值 \(\times\) 第 \(K\) 小的价值。因为 \(\mathrm{LZC}\) 非常的勤奋,所以这个问题就交给你了。
因为 \(\mathrm{LZC}\) 明天要去异世界旅游,所以你必须在考试结束之前完成程序,且 \(\times\) 定义为 \(\sum\limits_{i=1}^{{ans_1}^3}\ \sum\limits_{j=1}^{{ans_2}^3}\ ({ans_1}^3\mod\ i)*({ans_2}^3\mod j)\) 。由于 \(\mathrm{LZC}\) 买了高浓度压缩器,所以每一次对答案进行 \(\mod (10^4+7)\) 即可。
输入格式
第 \(1\) 行 \(1\) 个正整数 \(N\),以下 \(N\) 行代表每行 \(N\) 个正整数代表物品的价值。
第 \(N+2\) 行有 \(1\) 个正整数 \(M\) 代表询问次数。以下 \(M\) 行每行 \(5\) 个正整数 \(x_1,y_1,x_2,y_2,k\) 代表询问一个子矩阵。(\(x_1,y_1\) 是左上端点, \(x_2,y_2\) 是右下端点)
输出格式
每行一个正整数代表答案。
样例输入输出
2
3 4
3 4
1
1 1 2 2 1
6569
提示
样例 \(1\) 解释:
如图,这是物品矩阵:
3 4
3 4
我们查询 \(1,1,2,2\) 就是查询了全部矩阵,那么第 \(1\) 大的就是右下角(或者右上角)的 \(4\),第 \(1\) 小的就是左下角(或者左上角)的 \(3\)。
$\sum\limits_{i=1}{{3}3}\ \sum\limits_{j=1}{{4}3}\ ({3}3\mod i)*({4}3\mod j)\mod 10007 =\ $
\(\sum\limits_{i=1}^{{27}}\ \sum\limits_{j=1}^{64}\ (27\mod\ i)*(64\mod j)\mod 10007 =\ 6569\)
上面的东西可以打一个暴力算出。
var
i,j,ans:longint;
begin
for i:=1 to 27 do
for j:=1 to 64 do
begin
inc(ans,(27 mod i)*(64 mod j));
ans:=ans mod 10007;
end;
writeln(ans);
end.
数据范围 / 温馨提示
注意: \(x_1,y_1,x_2,y_2\)和最大价值 \(\ge 0\)。
|数据点|\(M\)|\(N\)|最大价值|时间限制|
|:-:||-|
|#\(1\)|\(10^2\)|\(2.5\times10^1\)|\(10^1\)|\(500\)ms|
|#\(2\)|\(10^2\)|\(10^3\)|\(10^3\)|\(500ms\)|
|#\(3\)|\(10^4\)|\(10^3\)|\(10^2\)|\(500ms\)|
|#\(4\)|\(2\times10^3\)|\(10^2\)|\(10^1\)|\(500ms\)|
|#\(5\)|\(2\times10^3\)|\(2\times10^2\)|\(10^1\)|\(500ms\)|
|#\(6\)|\(3\times10^4\)|\(5\times10^2\)|\(10^2\)|\(2500ms\)|
|#\(7\)|\(3\times10^3\)|\(5\times10^2\)|\(5\times10^2\)|\(2500ms\)|
|#\(8\)|\(2.1\times10^3\)|\(5\times10^2\)|\(10^3\)|\(4000ms\)|
|#\(9\)|\(4\times10^4\)|\(3\times10^2\)|\(5\times10^1\)|\(1500ms\)|
|#\(10\)|\(4\times10^4\)|\(3\times10^2\)|\(8\times10^1\)|\(2000ms\)|
|#\(11\)|\(5\times10^4\)|\(2\times10^2\)|\(10^1\)|\(2500ms\)|
|#\(12\)|\(2\times10^2\)|\(5\times10^2\)|\(10^2\)|\(500ms\)|
保证数据合法。建议常数优化。
至于 \(\sum\limits_{i=1}^{{ans_1}^3}\ \sum\limits_{j=1}^{{ans_2}^3}\ ({ans_1}^3\mod\ i)*({ans_2}^3\mod j)\) 就比较简单,参考此博文。
然后就剩下子矩阵求第 \(K\) 大的问题。如果还不知道整体二分求区间第 \(K\) 大的先看 \(I\),不知道矩阵如何求第 \(K\) 大的看 \(II\)。
\(I\)
思考一个问题 : 如果给你一个数组,只能给整个序列排序,如何求一个区间第 \(K\) 大。
这个问题比较难想吧。我们假设有这样一种排序,我们定义 \(l,r\) 为“数”的边界 (一开始 \(l\) 是 \(0\),\(r\) 是 \(\max num[i]\)),和 \(left,right\) 代表一个区间。如果我们定义一个 \(mid=(l+r) >> 1\),我们把小于 \(mid\) 的数放左边,大于的放右边,然后就形成了左右两边,右边的最小的一个数字肯定比左边的最大的一个是数字大。左右两边的边界叫 \(middle\),然后递归 \(left,middle\) 和 \(middle+1,r\)。这是一个普通的 \(n\log\ n\) 的排序,毋容置疑。(反过来归并排序)
在这期间,假如我询问一个区间 \(ques_l,ques_r\) 的第 \(K\) 大,而进入左边的且在 \(ques_l,ques_r\) 这个范围内的数字有 \(A\) 个。如果 \(A\) 比 \(K\) 小,就说明答案不在左边,必须让自己的 \(K\) 减去 \(A\)(因为前面已经有 \(A\) 个数字了,现在只有 \(K-A\) 个在右边)。否则就直接进左边。我们怎么判断在 \(ques_l,ques_r\) 之内呢?直接树状数组就 \(Ok\)啦。
\(II\)
直接把树状数组改成树状数组套树状数组就可以了。
\(Code\)
常数巨大
// luogu-judger-enable-o2
Uses math;
Const number=10007;
var
ans:array[-1..580000] of longint;
copy1,copy2,num:array[-1..580000,1..6] of longint;
tree:array[-1..1010,-1..1010] of longint;
i,j,n,m,x1,y1,x2,y2,k,cnt,maxn,ques:longint;
function lowbit(x:longint):longint; begin exit(x and -x); end;
procedure Insertx(x,y,k:longint); begin while y<=n do begin inc(tree[x,y],k); inc(y,lowbit(y)); end; end;
procedure Insert(x,y,k:longint); begin while x<=n do begin Insertx(x,y,k); inc(x,lowbit(x)); end; end;
function Sumx(x,y:longint):longint; begin Sumx:=0; while y>0 do begin inc(Sumx,tree[x,y]); dec(y,lowbit(y)); end; end;
function Sum(x,y:longint):longint; begin Sum:=0; while x>0 do begin inc(Sum,Sumx(x,y)); dec(x,lowbit(x)); end; end;
function Query(x1,y1,x2,y2:longint):longint; begin exit(Sum(x2,y2)-Sum(x1-1,y2)-Sum(x2,y1-1)+Sum(x1-1,y1-1)); end;
procedure Add(a,b,c,d,e,f:longint); begin inc(cnt); num[cnt,1]:=a; num[cnt,2]:=b; num[cnt,3]:=c; num[cnt,4]:=d; num[cnt,5]:=e; num[cnt,6]:=f; end;
function Sigma(n:longint):int64;
var
i,right,num:int64;
begin
Sigma:=0; i:=1;
while i<=n do
begin num:=n div i; right:=n div num; Sigma:=Sigma mod number; inc(Sigma,num*((i+right)*(right-i+1) div 2)); i:=right+1;
end;
end;
function answer(a,b:int64):int64;
begin
a:=(((a*a))-Sigma(a)) mod number;
b:=(((b*b))-Sigma(b)) mod number;
exit((a*b) mod number);
end;
procedure Divid(l,r,left,right:longint);
var
mid,tmp,i,j,i1:longint;
tot:array[1..2] of longint;
begin
if (l>r)or(left>right) then exit;
if l=r then
begin for i:=left to right do if num[i,6]>0 then ans[num[i,6]]:=l; exit;
end;
mid:=(l+r) >> 1; tot[1]:=0; tot[2]:=0;
for i:=left to right do
begin
if num[i,6]>0 then
begin
tmp:=Query(num[i,1],num[i,2],num[i,3],num[i,4]);
if tmp>=num[i,5] then begin
inc(tot[1]); copy1[tot[1]]:=num[i];
end else begin
dec(num[i,5],tmp); inc(tot[2]); copy2[tot[2]]:=num[i];
end;
end
else
begin
if num[i,5]<=mid then begin
inc(tot[1]); copy1[tot[1]]:=num[i]; Insert(num[i,1],num[i,2],1);
end else begin
inc(tot[2]); copy2[tot[2]]:=num[i];
end;
end;
end;
for i:=1 to tot[1] do if copy1[i,6]=0 then Insert(copy1[i,1],copy1[i,2],-1);
for i:=1 to tot[1] do num[left+i-1]:=copy1[i];
for i:=1 to tot[2] do num[left+tot[1]+i-1]:=copy2[i];
Divid(l,mid,left,left+tot[1]-1);
Divid(mid+1,r,left+tot[1],right);
end;
begin
read(n);
for i:=1 to n do for j:=1 to n do begin read(k); maxn:=max(maxn,k); Add(i,j,0,0,k,0); end;
read(m);
for i:=1 to m do begin read(x1,y1,x2,y2,k); inc(ques); Add(x1,y1,x2,y2,k,ques); inc(ques); Add(x1,y1,x2,y2,(x2-x1+1)*(y2-y1+1)-k+1,ques); end;
Divid(0,maxn+1,1,cnt); i:=0;
repeat inc(i,2); writeln(answer(ans[i-1]*ans[i-1]*ans[i-1],ans[i]*ans[i]*ans[i])); until i>=ques;
end.