异世界背包

\(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.
posted @ 2018-09-25 12:00 _ARFA 阅读(...) 评论(...) 编辑 收藏