前言:将高维数据拆分成低维的组合,以降低时间复杂度 OR 用已知的数据结构来简化题目。
NWERC 2020 I. Island Tour
题目类型:模拟、低维遍历
题意:n(400)个点形成圆环,每个人在点或者i到i+1的路上都有不同停留时间。三个人要同时遍历这个环,问是否存在三个起点,使得三人在遍历的过程不会相遇。
解析: 最暴力的方法:枚举所有的三个起点(n^3),模拟一遍(n),总时间复杂度O(n ^ 4)。
优化:按照上面的模拟思路,枚举所有的两个起点,模拟一遍,就可以处理出两个人之间的关系O(n^3),存起来g[a][b][i][j]就是a在i起点,b在j起点能否成功。那么按照这个方式处理三个人之间两两的关系。最后在枚举三个起点,O(1)来判断。总时间复杂度O(4 * n^3)
code:
#include <bits\stdc++.h>
#define ll long long
#define endl '\n'
#define maxn 444
using namespace std;
ll n,to[maxn],a[maxn][3];
bool c[3][3][maxn][maxn];
void scan(){
cin >> n ;
for(ll i = 1 ; i <= n ; ++i)cin >> to[i] ;
for(ll i = 1 ; i <= n ; ++i)cin >> a[i][0] ;
for(ll i = 1 ; i <= n ; ++i)cin >> a[i][1] ;
for(ll i = 1 ; i <= n ; ++i)cin >> a[i][2] ;
}
bool run(ll x,ll y,ll fx,ll fy){
ll hx = 0 , hy = 0;
ll xt = 0 , yt = 0;
while(hx < n-1 && hy < n-1){
ll nedx = a[fx][x] + to[fx] - xt;
ll nedy = a[fy][y] + to[fy] - yt;
if(nedx < nedy){
fx++;
hx++;
xt = 0;
yt += nedx;
}else if(nedx > nedy){
fy++;
hy++;
yt = 0;
xt += nedy;
}else {
xt = yt = 0;
fx++ , fy++;
hx++ , hy++;
}
if(fx > n)fx = 1;
if(fy > n)fy = 1;
if(fx == fy){
if(xt < a[fx][x] && yt < a[fy][y])return 0;
}
}
return 1;
}
void match(ll x,ll y){
for(ll i = 1 ; i <= n ; ++i){
for(ll j = 1 ; j <= n ; ++j){
if(i == j)continue;
c[x][y][i][j] = run(x,y,i,j);
}
}
}
void solve(){
match(0,1);
match(0,2);
match(2,1);
for(ll i = 1 ; i <= n ; ++i){
for(ll j = 1 ; j <= n ; ++j){
if(i == j)continue;
for(ll k = 1 ; k <= n ; ++k){
if(k == i || k == j)continue;
if(c[0][1][i][j] && c[0][2][i][k] && c[2][1][k][j]){
cout << i << " " << j << " " << k << endl ;
return;
}
}
}
}
cout << "impossible" << endl ;
}
int main(){
ios::sync_with_stdio(false);
///cin.tie(0);cout.tie(0);
ll t = 1;
while(t--){
scan();
solve();
}
Codeforces Round #574 (Div. 2) E. OpenStreetMap
题目传送门
题目类型:区间最小值、矩形拆线段
题意:给出一个n * m的矩阵,求出所有a * b的子矩阵中的最小值的和。
解析:很明显,博主并不会低于n^2复杂度的矩形最小值。只能把问题变简单点。先处理行最小值,在这个基础上处理列最小值就OK啦。
具体的:先把原矩阵处理成行最小值,比如a[i][j]代表的是a[i][j] 到 a[i][j+b-1]的最小值。就变成了一个n * (m-b+1)的行最小值矩阵。在对这个矩阵列求最小值,a[i][j]就代表左上角a[i][j] 右下角a[i+a-1][j+b-1]的矩形的最小值啦。最后对这个矩形求和即可。
区间最小值,可以用st表、线段树等,但是这题区间长度是固定的,可以用单调队列。
code:
#include <bits\stdc++.h>
#define ll int
#define maxn 3030
using namespace std;
void eninit();
ll n,m,a,b,ql,qr,q[maxn],h[maxn][maxn],dt[maxn][maxn];
long long g[maxn*maxn];
void scan(){
long long g0,x,y,z;
cin >> n >> m >> a >> b ;
cin >> g[0] >> x >> y >> z ;
for(ll i = 1 ; i <= n*m ; ++i)g[i] = (g[i-1]*x+y) % z;
for(ll i = 1 ; i <= n ; ++i)
for(ll j = 1 ; j <= m ; ++j)
h[i][j] = g[(i-1)*m + j-1];
}
void Deque(ll h[] , ll res[] ,ll m,ll b){ ///单调队列维护最小值
for(ll i = 0 ; i <= 3003 ; ++i)q[i] = 0;
ql = 1 , qr = 0;
for(ll j = 1 ; j <= m ; ++j){
while(ql <= qr && h[ q[qr] ] > h[j])--qr; ///最大值改这里的>
q[++qr] = j;
if(j >= b){
while(j - q[ql] + 1 > b)++ql;
res[j - b + 1] = h[ q[ql] ];
}
}
}
void rev(ll h[][maxn] , ll n,ll m){
ll maxx = max(n,m);
for(ll i = 1 ; i <= maxx ; ++i)
for(ll j = i+1 ; j <= maxx ; ++j)
swap(h[i][j] , h[j][i]);
}
void solve(){
for(ll i = 1 ; i <= n ; ++i)
Deque(h[i] , dt[i] ,m,b);
rev(dt , n,m-b+1);
for(ll i = 1 ; i <= m-b+1 ; ++i)
Deque(dt[i] , h[i] ,n,a);
rev(h , m-b+1,n-a+1);
long long ans = 0;
for(ll i = 1 ; i <= n-a+1 ; ++i)
for(ll j = 1 ; j <= m-b+1 ; ++j)
ans += h[i][j];
cout << ans << endl ;
}
int main(){
ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);
scan();
solve();
}
总结
写完博客继续写题了…看到类似的再来更新…