思想——高维拆分低维组合

本文探讨了两种算法优化技巧在解决实际问题中的应用。第一篇讲述了如何通过枚举和预处理降低时间复杂度,解决环形路径上的不相遇问题。第二篇介绍了将矩阵转换为行和列最小值,利用单调队列处理区间最小值,解决矩阵子区域最小值求和。这两篇文章都展示了在竞赛编程中如何巧妙地运用数据结构和算法。
摘要由CSDN通过智能技术生成

前言:将高维数据拆分成低维的组合,以降低时间复杂度 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();
}


总结

写完博客继续写题了…看到类似的再来更新…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值