C.E待补
A. 并行处理(前缀和)
思路:显然最短时间为
m
i
n
(
m
a
x
(
∑
i
=
1
j
a
i
,
∑
i
=
j
+
1
n
a
i
)
min(max(\sum_{i=1}^{j} a_i,\sum_{i=j+1}^{n}a_i)
min(max(∑i=1jai,∑i=j+1nai),
1
≤
j
≤
n
1\le j \le n
1≤j≤n,前缀和处理一下
复杂度:
O
(
n
)
O(n)
O(n)
C o d e : Code: Code:
#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
typedef long long ll;
typedef pair<int,int> PII;
const int N=1e6+10,mod=1e9+7;
int n,a[N];
int main(){
IOS;
cin>>n;
ll ans=1e18,ans1=0,sum=0;
for(int i=1;i<=n;i++) {
cin>>a[i];
sum+=a[i];
}
for(int i=1;i<=n;i++){
ans1+=a[i];
ans=min(ans,max(ans1,sum-ans1));
}
cout<<ans<<endl;
return 0;
}
B. 排列变换(思维)
思路:先考虑一个常见的问题:对于一个长度为
n
n
n的排列
a
a
a,每次选择一个数字往左移若干位,求将其变为递增序列需要的最小移动次数?
举个例子:
3
,
1
,
5
,
2
,
4
3,1,5,2,4
3,1,5,2,4,显然我们需要移动
3
3
3次,分别为
1
,
2
,
4
1,2,4
1,2,4,我们发现移动的原因显然是:
3
>
1
,
5
>
2
,
5
>
4
3>1,5>2,5>4
3>1,5>2,5>4,即:对于某个位置,如果它前面的序列的最大值大于它,它就需要移动位置
那对于这个问题,我们就把序列
b
b
b的值映射到一个递增序列中,即可把问题转换为上述问题
复杂度:
O
(
n
)
O(n)
O(n)
C o d e : Code: Code:
#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
typedef long long ll;
typedef pair<int,int> PII;
const int N=1e6+10,mod=1e9+7;
int n,a[N],c[N],b[N];
map<int,int>mp;
int main(){
IOS;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i],mp[b[i]]=i;
for(int i=1;i<=n;i++) c[i]=mp[a[i]];
int ans=0,pos=0;
for(int i=1;i<n;i++){
if(max(pos,c[i])>c[i+1]) {
ans++;
pos=max(pos,c[i]);
}
}
cout<<ans<<endl;
return 0;
}
D. 矩阵(数学+前缀和)
思路:对于矩阵乘法:
C
i
,
j
=
∑
k
=
1
n
A
i
,
k
∗
B
k
,
j
C_{i,j}=\sum\limits_{k=1}^{n} A_{i,k}*B_{k,j}
Ci,j=k=1∑nAi,k∗Bk,j,如果直接运算,复杂度
O
(
n
3
)
O(n^3)
O(n3)肯定
T
L
E
TLE
TLE
但对于每次询问
a
,
b
,
c
,
d
a,b,c,d
a,b,c,d构成的子矩阵和时,我们通过和式变换:
∑
i
=
a
c
∑
j
=
b
d
C
i
,
j
=
∑
i
=
a
c
∑
j
=
b
d
∑
k
=
1
n
A
i
,
k
∗
B
k
,
j
=
∑
k
=
1
n
[
(
∑
i
=
a
c
A
i
,
k
)
∗
(
∑
j
=
b
d
B
k
,
j
)
]
\sum\limits_{i=a}^{c}\sum\limits_{j=b}^{d}C_{i,j}=\sum\limits_{i=a}^{c}\sum\limits_{j=b}^{d}\sum\limits_{k=1}^{n}A_{i,k}*B_{k,j}=\sum\limits_{k=1}^{n}\Bigg[\Big(\sum\limits_{i=a}^{c}A_{i,k}\Big)*\Big(\sum\limits_{j=b}^{d}B_{k,j}\Big)\Bigg]
i=a∑cj=b∑dCi,j=i=a∑cj=b∑dk=1∑nAi,k∗Bk,j=k=1∑n[(i=a∑cAi,k)∗(j=b∑dBk,j)]
然后我们通过这个式子写出了如下
q
u
e
r
y
query
query函数:
ll query(int a,int b,int c,int d){
ll ans=0;
for(int k=1;k<=n;k++){
ll ans1=0,ans2=0;
for(int i=1;i<=n;i++) ans1+=A[i][k];
for(int i=1;i<=n;i++) ans2+=B[k][i];
ans+=ans1*ans2;
}
return ans;
}
然后就继续
T
L
E
TLE
TLE,显然上述做法复杂度是
O
(
m
∗
n
2
)
O(m*n^2)
O(m∗n2),不可行
我们考虑对上述做法进行优化,我们发现通过预处理前缀和可以实现:
∑
i
=
a
c
A
i
,
k
=
s
u
m
c
,
k
−
s
u
m
a
−
1
,
k
∑
j
=
b
d
B
k
,
j
=
s
u
m
k
,
d
−
s
u
m
k
,
b
−
1
\sum\limits_{i=a}^{c}A_{i,k}=sum_{c,k}-sum_{a-1,k}\ \sum\limits_{j=b}^{d}B_{k,j}=sum_{k,d}-sum_{k,b-1}
i=a∑cAi,k=sumc,k−suma−1,k j=b∑dBk,j=sumk,d−sumk,b−1
将这个求和符号优化掉之后,复杂度为:
O
(
m
∗
n
)
O(m*n)
O(m∗n),可行
C o d e : Code: Code:
#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
typedef long long ll;
typedef pair<int,int> PII;
const int N=2e3+10,mod=1e9+7;
int n,m,a,b,c,d,A[N][N],B[N][N],sum1[N][N],sum2[N][N];
template<class T>
void read(T &x){
x=0; char c; int sign=1;
do{ c=getchar(); if(c=='-') sign=-1;}while(!isdigit(c));
do{ x=x*10+c-'0',c=getchar();}while(isdigit(c));
x*=sign;
}
ll query(int a,int b,int c,int d){
ll ans=0;
for(int k=1;k<=n;k++){
ll ans1=sum1[c][k]-sum1[a-1][k];
ll ans2=sum2[k][d]-sum2[k][b-1];
ans+=ans1*ans2;
}
return ans;
}
int main(){
read(n),read(m);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
read(A[i][j]);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
read(B[i][j]);
}
}
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++) sum1[i][k]=sum1[i-1][k]+A[i][k];
for(int i=1;i<=n;i++) sum2[k][i]=sum2[k][i-1]+B[k][i];
}
while(m--){
read(a),read(b),read(c),read(d);
if(a>c)swap(a,c);
if(b>d)swap(b,d);
printf("%lld\n",query(a,b,c,d));
}
return 0;
}