ARC104部分题目简要题解
虽然早上太困了,vp打得很垃圾
但是自己手玩出E,F还有有点激动的
D.
分数规划后多重背包即可
不是很难,但是要记住多重背包复杂度是可以优化的qwq
看到分数的话,一般要么就斜率优化,要么就分数规划
E.
可以先枚举数的相对大小关系(包括有多少个数互不相同)
然后可以根据差分数组将值域分成若干个区间,根据这个关系继续爆搜,注意到若干个上下界相同的区间中选一个单调递增的子序列的方案数就是一个组合数
因为范围很小,自己想了一个爆搜套爆搜的做法,跑的飞快,思维难度为0,但是巨难写(写了2h+)
比较好的做法可以参考这篇 blog ( orz xgzc , orz Itst) ARC104 E
F.
很有意思的题
题意简述:定义对于一个大小为 \(n\) 的数组 H, \(P_i\) 为 最大的 \(j\),满足\((H_j > H_i)\),如果不存在,令 \(P_i = -1\),现在要求你计算对于所有满足 \(H_i \leq X_i\)的 H 数组,本质不同的 P 有多少个
\(n \leq 100,X_i \leq 1e5\)
solution
我们不妨推一下 \(P_i\) 等于某个值 \(x\) ,需满足什么
可以发现,若 \(P_i = j\),则 \(H_j \ge H_i + 1\)
若\(P_i = -1\),那么 \(H_i\) 必然 \(\ge \max_{j = 1}^{i - 1}H_j\)
可以发现根据 \(P_i\) ,我们可以连一颗以\((H_i,i)\)为二维关键字排序的笛卡尔树
那么每个 \(P\) 数组都会对应一颗笛卡尔树
那么问题转化成了有多少个合法的本质不同笛卡尔树(即定义两个笛卡尔树相同,当且仅当父子关系都相同,不考虑键值)
我们考虑假设一个笛卡尔树的根当前允许的最大键值为 \(H\),
-
对于左儿子 \(lc\) ,它的最大键值则为 \(min(H,X[lc])\)
-
对于右儿子\(rc\), 它的最大键值则为\(min(H - 1,X[rc])\)
若任意一个点,允许的最大键值\(\leq 0\),则不合法,否则合法
于是可以用一个区间 \(dp\) 来解决这个问题,令dp[l][r][h][0/1]
表示当前解决 \([l,r]\) 这个区间,根在最左边或者最右边,根的权值为\(h\)的方案数
最后答案即为 \(\sum_{rt = 1}^n dp(l,rt,X[rt],1) * dp(rt,n,X[rt],0)\)
复杂度\(O(n^4)\)
#include <bits/stdc++.h>
using namespace std;
int read() {
char c = getchar();
int x = 0;
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48,c = getchar();
return x;
}
const int _ = 1e2 + 7;
int dp[_][_][_][2];/*0 at left,1 at right*/
bool vis[_][_][_][2];
int X[_];int n;
const int mod = 1e9 + 7;
void add(int &x,int y) {
x += y - mod;
x += (x >> 31) & mod;
}
int calc(int l,int r,int h,int o) {
if(vis[l][r][h][o]) return dp[l][r][h][o];
if(h <= 0) return 0;
if(l == r) return 1;/*为空*/
vis[l][r][h][o] = 1;
int dl = l,dr = r;
if(o == 1) dr--;
else dl++;
int H = h - (o == 0);/*如果是右儿子的话,最大可允许值-1*/
for(int rt = dl; rt <= dr; ++rt){
// cout<<rt<<' '<<calc(dl,rt,min(X[rt],h),1)<<' '<<calc(rt,dr,min(X[rt],h - 1),0)<<'\n';
add(dp[l][r][h][o],1ll * calc(dl,rt,min(X[rt],H),1) * calc(rt,dr,min(X[rt],H),0) % mod);
}
return dp[l][r][h][o];
}
int main() {
n = read();
for (int i = 1; i <= n; ++i) X[i] = min(read(),n);
int ans = 0;
for (int rt = 1; rt <= n; ++rt) {
add(ans,1ll * calc(1,rt,X[rt],1) * calc(rt,n,X[rt],0) % mod);
}
cout << ans << '\n';
return 0;
}