[USACO24OPEN] Smaller Averages G (单调性优化dp)

来源

题目

Bessie 有两个长度为 N的数组(1≤N≤500)。第一个数组的第 i 个元素为 ai​(1≤ai​≤10^6),第二个数组的第 i个元素为bi​(1≤bi​≤10^6)。

Bessie 希望将两个数组均划分为若干非空子数组,使得以下条件成立。

  1. 每个元素恰属于 1个子数组。
  2. 两个数组划分为相同数量的子数组。令第一个和第二个数组被划分为的子数组数量为 k(即,第一个数组被划分为恰好 k个子数组,第二个数组被划分为恰好 k 个子数组)。
  3. 对于所有1≤i≤k,第一个数组左数第 i 个子数组的平均值小于或等于第二个数组左数第 i个子数组的平均值。

计算她有多少种方式在满足限制的情况下将两个数组划分为非空子数组,对 10^9+7取模。两种划分方式被认为是不同的,如果子数组的数量不同或是某个元素属于不同的子数组。

输入格式

输入的第一行包含 NN。

第二行包含 a_1,a_2,\ldots,a_Na1​,a2​,…,aN​。

第三行包含 b_1,b_2,\ldots,b_Nb1​,b2​,…,bN​。

输出格式

输出在满足限制的情况下将两个数组划分为非空子数组的方法数模 10^9+7109+7 的余数。

思路

        首先不管复杂度,先考虑n^{4}的dp

        设dp_{i, j}为a数组的第i位和 b数组 的第 j 位相匹配的时方案数,那么dp_{i,j}应该为满足条件的数字对dp_{l, k}的和,条件就是a数组l到i的平均值,小于或等于b数组k到j的平均值,算平均值的时候要把除法转化为乘法,避免精度的缺失,对于这个dp只要暴力枚举i,j,l,k就行。

        对于题目所要求的n^{3}的复杂度,可以考虑只枚举三个变量,考虑用单调性优化掉其中的一维,但是对于一个数组的每一段的平均值必然不满足单调性,可以想到固定区间的右端点,把以此为右端点的区间全部根据区间的平均值进行排序。将两个数组的每一个i,j都做这样的预处理之后。

        我们之前是枚举l和k,现在只需要枚举k,对于固定的k,l一个是随着k 的增加而增加或者不变的,这样就优化了枚举l 的复杂度。

#include<bits/stdc++.h>

using namespace std;
#define int long long

const int N = 666;
const int mod = 1e9 + 7;


int n;
int l;
int a[N], b[N], suma[N], sumb[N];
int dp[N][N], sum[N][N];
struct node {
    int l, r, w;
} aa[N][N], bb[N][N];

bool cmp(node xx, node yy) {
    return xx.w * (yy.r - yy.l + 1) <= yy.w * (xx.r - xx.l + 1);
}

void solve() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        suma[i] = suma[i - 1] + a[i];
    }
    for (int i = 1; i <= n; i++) {
        cin >> b[i];
        sumb[i] = sumb[i - 1] + b[i];
    }
    for (int i = 1; i <= n; i++) {
        for (int j = i; j <= n; j++) {
            aa[j][i].l = i;
            aa[j][i].r = j;
            aa[j][i].w = suma[j] - suma[i - 1];
            bb[j][i].l = i;
            bb[j][i].r = j;
            bb[j][i].w = sumb[j] - sumb[i - 1];
        }
    }
    for (int i = 1; i <= n; i++) {
        sort(aa[i] + 1, aa[i] + 1 + i, cmp);
        sort(bb[i] + 1, bb[i] + 1 + i, cmp);
    }
    dp[0][0] = 1;

    for (int i = 1; i <= n; i++) {
        memset(sum, 0, sizeof(sum));

        for (int k = 1; k <= i; k++) {
            for (int j = 1; j <= n; j++) {
                sum[k][j] = (sum[k - 1][j] + dp[aa[i][k].l - 1][j - 1]) % mod;

            }
        }

        for (int j = 1; j <= n; j++) {
            l = 1;
            for (int k = 1; k <= j; k++) {
                while (l <= i) {
                    if (cmp(aa[i][l], bb[j][k])) {
                        l++;
                    } else break;
                }
                if (l!=1)
                    dp[i][j] = (dp[i][j] + sum[l - 1][bb[j][k].l]) % mod;
            }
        }
    }
    cout << dp[n][n];

    
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    solve();
    return 0;
}

        

上一篇:vb.net&cad二开自学笔记1:万里长征第一步Hello CAD!


下一篇:python爬虫加入进度条