牛客练习赛46-B
题目
给两个数组a, b,长度n, m。构成二维矩阵 c [ i ] [ j ] = a [ i ] ∗ b [ j ] c[i][j] = a[i] * b[j] c[i][j]=a[i]∗b[j],求所有子矩阵满足矩阵和在(L,R)之间的个数。
1<=n,m<=1000,1<=L<=R<=1e18
1<=a[i],b[i]<=1e6
分析
做法一:
(二分有时候挺难想。。)
考虑求出二维前缀和sum[i][j],对于以(x1, y1),(x2, y2)为顶点的矩阵和
S
=
s
u
m
[
x
2
]
[
y
2
]
+
s
u
m
[
x
1
−
1
]
[
y
1
−
1
]
−
s
u
m
[
x
1
−
1
]
[
y
2
]
−
s
u
m
[
x
2
]
[
y
1
−
1
]
S = sum[x2][y2] + sum[x1-1][y1-1] - sum[x1-1][y2] - sum[x2][y1-1]
S=sum[x2][y2]+sum[x1−1][y1−1]−sum[x1−1][y2]−sum[x2][y1−1]
但是根据矩阵c构成方式,可以只求出a,b数组前缀和suma, sumb。上述矩阵和表示为
S
=
(
s
u
m
a
[
x
2
]
−
s
u
m
a
[
x
1
−
1
]
)
∗
(
s
u
m
b
[
y
2
]
−
s
u
m
b
[
y
1
−
1
]
)
S = (suma[x2] - suma[x1-1])*(sumb[y2] - sumb[y1-1])
S=(suma[x2]−suma[x1−1])∗(sumb[y2]−sumb[y1−1])
也就是每个子矩阵都可以用上式表示
简化一下:S = F(x1, x2) * G(y1, y2)
可以预处理 G所有取值,排序。
枚举x1,x2,对于得到的每一个 F值,二分G来找范围。
最后复杂度O(n * n * log(m*m)) 221ms
做法二:
提交列表有 86ms做法
预处理F,G所有取值排序,
对于每个G数组的值,找出F数组对应的上下限,相减就是一次的答案。
最后将每次答案求和输出。
①:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define d(x) cout << (x) << endl
#pragma GCC diagnostic error "-std=c++11"
using namespace std;
typedef long long ll;
const double eps = 1e-8;
const int mod = 1e9 + 7;
const int N = 1e3 + 10;
ll n, m, L, R;
ll suma[N], sumb[N];
ll f[N*N];
ll cnt = 0;
ll check(ll pos)
{
ll id = 0;
int l = 1, r = cnt;
while(l <= r){
int mid = l + r >> 1;
if(f[mid] <= pos){
id = mid;
l = mid + 1;
}else{
r = mid - 1;
}
}
return id;
}
ll sum(ll pos) //枚举a,二分计算f数组pos值的下标,返回所有下标之和
{
ll ans = 0;
for(int i = 1; i <= n; i++){
for (int j = i; j <= n; j++){
ll x = suma[j] - suma[i - 1];
ans += check(pos / x);
}
}
return ans;
}
int main()
{
scanf("%lld%lld%lld%lld", &n, &m, &L, &R);
for(int i = 1, x; i <= n; i++){
scanf("%d", &x);
suma[i] = suma[i - 1] + x;
}
for(int i = 1, x; i <= m; i++){
scanf("%d", &x);
sumb[i] = sumb[i - 1] + x;
}
for(int i = 1; i <= m; i++){ //y1
for (int j = i; j <= m; j++){ //y2
f[++cnt] = sumb[j] - sumb[i - 1];
}
}
sort(f + 1, f + cnt + 1);
printf("%lld\n", sum(R) - sum(L-1));
return 0;
}
②:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define d(x) cout << (x) << endl
#pragma GCC diagnostic error "-std=c++11"
using namespace std;
typedef long long ll;
const double eps = 1e-8;
const int mod = 1e9 + 7;
const int N = 1e3 + 10;
ll n, m, L, R;
ll suma[N], sumb[N];
ll f[N * N], g[N * N];
ll cf = 0, cg = 0;
ll ans;
void solve()
{
int p1, p2;
p1 = p2 = cf;
for(int i = 1; i <= cg; i++){
while((p1 - 1) > 0 && g[i] * f[p1 - 1] >= L)
p1--;
while(p2 > 0 && g[i] * f[p2] > R)
p2--;
ans += max(p2 - p1 + 1, 0);
}
printf("%lld", ans);
}
int main()
{
scanf("%lld%lld%lld%lld", &n, &m, &L, &R);
for(int i = 1, x; i <= n; i++){
scanf("%d", &x);
suma[i] = suma[i - 1] + x;
}
for(int i = 1, x; i <= m; i++){
scanf("%d", &x);
sumb[i] = sumb[i - 1] + x;
}
for(int i = 1; i <= m; i++){ //y1
for (int j = i; j <= m; j++){ //y2
f[++cf] = sumb[j] - sumb[i - 1];
}
}
for(int i = 1; i <= n; i++){
for(int j = i; j <= n; j++){
g[++cg] = suma[j] - suma[i - 1];
}
}
sort(f + 1, f + cf + 1);
sort(g + 1, g + cg + 1);
solve();
return 0;
}