Solution -「CF 1621G」Weighted Increasing Subsequences

cirnovsky /

link。

一个 dp(拜谢 ly)和切入点都略有不同的做法,并不需要观察啥性质。

原问题针对子序列进行规划,自然地想到转而对前缀进行规划。接下来我们考虑一个前缀 [1,i][1, i] 以及一个 j[1,i]j \in [1, i] 对答案的贡献,可以写出 cont(j to [1,i])=[maxi<kak>aj]×the number of LISs containing j indexed in [1,i]\displaystyle \textit{cont}(j \text{ to } [1, i]) = [\max_{i < k} a_k > a_j] \times \text{the number of LISs containing } j \text{ indexed in } [1, i]

这个可以做两个 dp 解决,第一个好解决的静态 dp,即结束在 jj 的 LIS 方案数;第二个 dp 有些烦:jj 在动,我们考虑的前缀 [1,i][1, i] 也在移动。

到这里其实换一下着手点第二个 dp 就变成静态的了,即考虑位置 jj,直接算 (j,i)(j, i) 的贡献即可,其中 ii 是「满足 ai>aja_i > a_j 的最大的 ii」。于是我们的第二个 dp 就可以被描述为从 jj 开始,结束在 ii 之前(不包括,因为要保证 maxi<kak>aj\max_{i < k} a_k > a_j)的 LIS 方案数。答案即 i=1ndpi×dpi\displaystyle \sum_{i=1}^n dp_i \times dp'_i

第二个 dp 的具体实现,还需要一个辅助的 dp,其定义是第二个 dp 的定义中去掉贡献范围上界(即 ii),转移画画图就能理解了。

用下 Fenwick Tree 啥的就能 O(nlog2n)O(n \log_2 n) 了。

using modint = modint1000000007;
int n, a[200100], pos[200100], id[200100];
modint dp[200100], dp2[200100], dp3[200100], bit[200100], bit2[200100];
void cng(int x, modint w) {
    for (; x<=n; x+=x&-x) {
        bit[x] += w;
    }
}
modint qry(int x) {
    modint res = 0;
    for (; x; x-=x&-x) {
        res += bit[x];
    }
    return res;
}
void cng2(int x, modint w) {
    for (; x<=n; x+=x&-x) {
        bit2[x] += w;
    }
}
modint qry2(int x) {
    modint res = 0;
    for (; x; x-=x&-x) {
        res += bit2[x];
    }
    return res;
}
void solve() {
    cin >> n;
    bastr<int> dsc;
    for (int i=1; i<=n; ++i) {
        cin >> a[i];
        dsc += a[i];
    }
    sort(dsc.begin(), dsc.end());
    dsc.erase(unique(dsc.begin(), dsc.end()), dsc.end());
    for (int i=1; i<=n; ++i) {
        a[i] = lower_bound(dsc.begin(), dsc.end(), a[i])-dsc.begin()+1;
    }
    iota(id+1, id+n+1, 1);
    sort(id+1, id+n+1, [&](int x, int y) {
        return a[x] < a[y];
    });
    for (int i=1,now=n; i<=n; ++i) {
        while (now >= 1 && a[now] <= a[id[i]]) {
            now--;
        }
        pos[id[i]] = now;
    }
    for (int i=1; i<=n; ++i) {
        bit[i] = 0;
    }
    for (int i=1; i<=n; ++i) {
        dp[i] = qry(a[i]-1)+1;
        cng(a[i], dp[i]);
    }
    for (int i=1; i<=n; ++i) {
        bit[i] = bit2[i] = 0;
    }
    modint ans = 0;
    for (int i=n; i>=1; --i) {
        dp2[i] = qry(cs(dsc)-a[i])+1;
        cng(cs(dsc)-a[i]+1, dp2[i]);
        if (pos[i] > i) {
            dp3[i] = qry(cs(dsc)-a[pos[i]]+1)+qry2(a[pos[i]]-1)-qry2(a[i]);
            cng2(a[i], dp3[i]);
        }
        else {
            dp3[i] = 0;
        }
        ans += dp[i]*dp3[i];
    }
    cout << ans.val() << "\n";
}