A. Recycling Center

思路

  • 将所有袋子按重量升序排序。
  • 第 i 小的袋子在被销毁前会被乘 $2^{i-1}$。其判定是否大于 c 只取决于原始重量是否大于 $\frac{c}{2^{i-1}}$。
  • 从最轻到最重遍历,若当前袋子经过倍增仍大于 c,则答案 +1。

ac code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
LL n, c;
void solve() {
    cin >> n >> c;
    vector<LL> a(n);
    for(auto& x : a) cin >> x;
    
    vector<int> d;
    for(auto x : a){
        if(x > c){
        	d.push_back(-1);
        	continue;
        }
        LL k = 0;
        while((x << 1) <= c) x <<= 1, ++ k;
        d.push_back(k);
    }
    sort(d.begin(), d.end());
    
    int t = 0, ans = 0;
    for(int v : d){
        if(v >= t) ++ t;
        else ++ ans;
    }
    cout << ans << '\n';
}

B. Deque Process

思路

贪心 + 小范围前瞻

限制只作用于 连续 $5$ 个元素,因此只需关注最新选出的 $4$ 个元素与下一次即将放入的元素之间的关系。核心贪心思想是:

维护当前已选序列的后缀长度 $\le 4$,保证它不是严格单调。

当后缀已经不是单调时,两端任意一值都不会形成坏序列,可以任选其一。

当后缀正好严格递增/递减时,只要下一元素打断这一趋势即可;至少有一端一定能做到这一点。

为了避免走进死胡同(当前合法,但未来无法继续),再做一步前瞻检查:假设本次取值为 $v_1$,看剩余两端的任意下一取值 $v_2$ 是否仍可避免生成坏序列。如果存在可行,则立即确定本次选择。

ac code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
vector<int> p, q;
int n;
bool check1(int v)
{
    int m = q.size();
    if(m < 4) return 0;
    int a = q[m - 4], b = q[m - 3],
        c = q[m - 2], d = q[m - 1];
    if(a < b && b < c && c < d && d < v) return 1;
    if(a > b && b > c && c > d && d > v) return 1;
    return 0;
}
bool check2(int v1, int v2)
{
    int m = q.size();
    if(m < 3) return 0;
    int a = q[m - 3], b = q[m - 2], c = q[m - 1];
    if(a < b && b < c && c < v1 && v1 < v2) return 1;
    if(a > b && b > c && c > v1 && v1 > v2) return 1;
    return 0;
}
char foo(int l, int r)
{
    for(char f : {'L', 'R'}){
        int v1 = (f == 'L') ? p[l] : p[r];
        if(check1(v1)) continue;
        int nl = l + (f == 'L');
        int nr = r - (f == 'R');
        if(nl > nr) return f;
        int vL = p[nl], vR = p[nr];
        if(!check2(v1, vL) || !check2(v1, vR)) return f;
    }
    return 'L';
}
void solve()
{
    p.clear(), q.clear();
	cin >> n;
	p.resize(n);
    for(int& i : p) cin >> i;
    
    int l = 0, r = n - 1;
    string s;
    while(l <= r){
        char c = foo(l, r);
        if(c == 'L') q.push_back(p[l]), ++ l;
        else q.push_back(p[r]), -- r;
        s.push_back(c);
    }
    
    cout << s << '\n';
}

C. Leftmost Below

题目重述

初始时有长度为 $n$ 的数组 $a=[0,0,\dots,0]$。一次操作如下:

  1. 任选整数 $x$,要求 $x>\min(a)$。
  2. 设 $i$ 为最小的下标,使得 $a_i<x$ 并且 $a_j\ge x;(1 \le j<i)$。
  3. $a_i = a_i + x$ 。

操作次数不限,问能否把 $a$ 变成给定目标数组 $b=[b_1,\dots,b_n]$。

思路

  • 设当前已确定前 $k$ 个位置的最终值,记

    $$ m=\min_{1\le j\le k}b_j . $$

    当把 $a_{k+1}$ 加到 $b_{k+1}$ ,增量记为 $x$。

    由题可知

    1. $a_{k+1}^{\text{旧}}<x\le m$(因为 $x$ 不会作用到已完成的前缀,且 $m$ 是它们的最小值);
    2. $b_{k+1}=a_{k+1}^{\text{旧}}+x<2x$。

    因此

    $$ b_{k+1}\le 2x-1\le 2m-1 . $$

  • 即有目标数组可达,当且仅当

    $$ \boxed{b_i\le 2\cdot\min_{1\le j<i}b_j-1\quad(\forall, i\ge 2)}. $$

ac code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
int n;
void solve()
{
    cin >> n;
    vector<LL> b(n);
    for(auto& x : b) cin >> x;
    
    LL mi = b[0];
    for(int i = 1; i < n; i ++){
        if(b[i] > (mi << 1) - 1){
        	cout << "NO\n";
        	return ;
        }
        mi = min(mi, b[i]);
    }
    
    cout << "YES\n";
}

D. Sum of LDS

题目重述

给定排列 $p_1,\dots ,p_n$,满足

$$ \max(p_i,,p_{i+1}) > p_{i+2}\qquad(1\le i\le n-2). $$

对每个区间 $[l,r]$ 记

$$ f(l,r)=\text{区间 }p_l,\dots ,p_r\text{ 的LDS长度}, $$

$$ \mathrm{Ans}=\sum_{1\le l\le r\le n} f(l,r). $$

思路

  • 自右向左扫描区间,能够选入最长下降子序列的元素必定是后缀最大。

  • 在题目给定的三元约束下,这些极大值本身就构成LDS,长度等于极大值个数。

    因此

    $$ f(l,r)=\bigl|{,j\mid l\le j\le r,;p_j>\max_{k>j}p_k}\bigr|. $$

具体

$$ \operatorname{next}(i)=\min{,j>i\mid p_j>p_i},\qquad\text{若不存在取 }n+1. $$

  • 若区间右端 $r\le\operatorname{next}(i)-1$,则 $p_i$ 是该区间的右侧极大值。
  • 区间左端 $l$ 只要 $\le i$ 即可。

故 $p_i$ 对答案的贡献为

$$ i\cdot\bigl(\operatorname{next}(i)-i\bigr). $$

答案可表示为

$$ \boxed{\displaystyle \mathrm{Ans}=\sum_{i=1}^{n} i\bigl(\operatorname{next}(i)-i\bigr) }, $$

其中 $\operatorname{next}(i)$ 为 $i$ 右侧第一个比 $p_i$ 大的位置。 单调栈线性即可求得。

ac code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
int n;
void solve()
{
    cin >> n;
    vector<int> a(n), nx(n), st;
    for(int& i : a) cin >> i;
    for(int i = n - 1; ~i; i --){
        while(!st.empty() && a[st.back()] < a[i])
        	st.pop_back();
        	
        nx[i] = st.empty() ? n : st.back();
        st.push_back(i);
    }
    
    LL ans = 0;
    for(int i = 0; i < n; i ++)
        ans += 1LL * (i + 1) * (nx[i] - i);

    cout << ans << '\n';
}