NewOJ Week 5

NewOJ Week 5

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 1jn,前缀和处理一下
复杂度: 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=1nAi,kBk,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=acj=bdCi,j=i=acj=bdk=1nAi,kBk,j=k=1n[(i=acAi,k)(j=bdBk,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(mn2),不可行
我们考虑对上述做法进行优化,我们发现通过预处理前缀和可以实现:
∑ 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=acAi,k=sumc,ksuma1,k j=bdBk,j=sumk,dsumk,b1
将这个求和符号优化掉之后,复杂度为: O ( m ∗ n ) O(m*n) O(mn),可行

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学不会数据库

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值