zoukankan      html  css  js  c++  java
  • ZJOI2020 序列

    ZJOI2020 序列 [* hard]

    给定长度为 (n) 的序列,你可以:

    1. 选择一个区间 ([l,r]),给区间所有数减 (1)
    2. 选择一个区间 ([l,r]),给区间所有奇数下标的值减 (1)
    3. 选择一个区间 ([l,r]),给区间所有偶数下标的值减 (1)

    求最小操作次数,使得所有数变成 (0)

    (nle 10^6,Tle 10,a_ile 10^9)

    Solution

    神仙贪心题,大概是欣赏的咕咕第一篇题解。

    定义操作 (1) 为直线操作,即将 (a_{l},a_{l+1}...a_r) 集体减 (1)

    定义操作 (2) 为跳线操作,即将 (a_{l},a_{l+2}...) 集体减 (1)

    可以注意到一个强有力的性质:

    • 假设当前有 (a_1,a_2>0),那么至少有 (1) 次操作是从 (a_1) 处开始的直线。

    考虑反证,如果没有,那么只有这样的情况:

    1. 操作是 (a_1) 跳线,(a_2) 直线。

    显然等价于 (a_1) 直线,(a_3) 跳线(多的再直线)。

    1. 操作是 (a_1) 跳线,(a_2) 跳线。

    显然不会优于,先 (a_1) 直线若干次,再 (a_1/a_2) 跳线。

    这里的具体论证略为复杂,感性理解就好(

    于是得到了一个贪心,对于 (a_1) 而言,操作 (t=min(a_1,a_2)) 次直线,剩下的都直接跳线。

    但是这样难以拓展到所有数的情况。

    于是维护 (A,B,C) 表示可以免费(a_i) 出发的直线数为 (A),跳线数为 (B),从 (i+1) 出发的跳线数为 (C)

    这样我们可以直接对 (a_i) 进行处理。

    考虑如何拓展到 (a_{i+1})

    假设 (A+Cle a_{i+1}),那么我们是可以直接交换 (B,C) 并往后处理的。

    假设 (A+C>a_{i+1}),那么我们相当于需要确定一个最优的 (x,y) 使得 (x+y=a_{i+1},xle A,yle C) 使得其可以方便的继续操作(因为多余的将无法衍生出去)。

    (K=A+C-a_{i+1}),即我们多余的操作次数,那么我们会发现至少有 (A-K) 次直线和 (C-K) 条跳线是一定会执行的。

    注意到 (A+C-K=(A-K)+(C-K)+K=a_{i+1}),所以我们还需要执行 (K) 次操作,这 (K) 次操作由之前的 (A)(C) 提供,同时是免费的,换而言之我们希望这 (K) 次操作由 (i+1)(i+2)(即往后的讨论)来帮我们解决此问题。

    于是我们将答案减去 (K),将 (A)(C) 均减去 (K) 然后继续处理即可。

    然而一个矛盾的点是,(A)(C) 不能为负数。

    没关系,假设 (A<K),那么我们会发现有 (C) 类操作一定有 (K-A) 次没有用在 (i+1) 处(即假设多余操作均为 (A) 类,那么额外多余的操作一定还有 (K-A) 次)。

    这样的话可以将 (C) 减去 (K-A),此时的 (K'=A+C'-a_{i+1}=K-(K-A)=A),所以将 (K) 设为 (A) 即可。

    同理于 (C)

    至此,本题在线性的复杂度完美解决。

    (Code:)

    #include<bits/stdc++.h>
    using namespace std ;
    #define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
    #define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
    #define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
    #define re register
    #define int long long
    int gi() {
    	char cc = getchar() ; int cn = 0, flus = 1 ;
    	while( cc < '0' || cc > '9' ) {  if( cc == '-' ) flus = - flus ; cc = getchar() ; }
    	while( cc >= '0' && cc <= '9' )  cn = cn * 10 + cc - '0', cc = getchar() ;
    	return cn * flus ;
    }
    const int N = 1e6 + 5 ; 
    int T, n, a[N] ;  
    signed main()
    {
    	T = gi() ; 
    	while( T-- ) {
    		n = gi() ; 
    		rep( i, 1, n ) a[i] = gi() ; 
    		int A = 0, B = 0, C = 0, ans = 0 ;
    		a[n + 1] = 0 ; 
    		for(re int i = 1; i <= n; ++ i) {
    			a[i] -= min( A + B, a[i] ) ;
    			int ta = min( a[i], max( 0ll, a[i + 1] - A - C ) ), tb = max( 0ll, a[i] - max( 0ll, a[i + 1] - A - C ) ) ;
    			ans += (ta + tb), A += ta, B += tb ; 
    			if( A + C > a[i + 1] ) {
    				int K = A + C - a[i + 1] ;
    				int ra = A - K, rc = C - K ;
    				if( ra < 0 ) C -= (K - A), K = A ;
    				if( rc < 0 ) A -= (K - C), K = C ;
    				A -= K, C -= K, ans -= K ; 
    			}
    			swap( B, C ) ; 
    		}
    		cout << ans << endl ; 
    	}
    	return 0 ;
    }
    
  • 相关阅读:
    docker 批量删除
    ML
    hdu 1465:不容易系列之一(递推入门题)
    sdut 2162:The Android University ACM Team Selection Contest(第二届山东省省赛原题,模拟题)
    sdut 2163:Identifiers(第二届山东省省赛原题,水题)
    hdu 2108:Shape of HDU(计算几何,判断多边形是否是凸多边形,水题)
    hrbustoj 1545:基础数据结构——顺序表(2)(数据结构,顺序表的实现及基本操作,入门题)
    hdu 1312:Red and Black(DFS搜索,入门题)
    hrbustoj 1429:凸多边形(计算几何,判断点是否在多边形内,二分法)
    poj 1113:Wall(计算几何,求凸包周长)
  • 原文地址:https://www.cnblogs.com/Soulist/p/13732003.html
Copyright © 2011-2022 走看看