zoukankan      html  css  js  c++  java
  • CF1172F

    P5609 [Ynoi2013]对数据结构的爱 / CF1172F [* hard]

    题意见 link

    Solution

    查询的答案其实就是区间和减去 (k imes p)(k) 表示被减 (p) 的次数。

    考虑线段树,我们考虑做完一个区间后肯定会剩余和 (S),然后我们将 (S) 输入给一个区间后肯定会减去若干个 (p)

    假设区间长度为 (r-l+1),那么我们至多减去 (r-l+1)(p),同时,我们减去的 (p) 的数量关乎于初始输入的参数,这样会形成一个分段函数,我们假设进行了预处理,那么就可以直接通过二分来进行查询。

    所以我们的处理思路就是将这些分段函数预处理出来,然后查询就直接二分 (log n) 个区间即可,复杂度 (mathcal O(qlog^2 n))

    考虑预处理,我们将小区间合并为大区间。

    (c_i) 表示最小的 (S) 使得输入进来后被减去了 (i)(p)

    我们知道了 (c_{[ m l,mid]},c_{[ m mid+1,r]}),我们希望得到 (c_{[l,r]})

    对于一组 (x,y),我们可以合并得到 (c_{x+y}),假设此时以 (S) 作为初值输入进来,我们有 (S-x imes p+sum_{l}ge c_y,Sge c_x) 那么就可以更新一次答案。

    于是有 (c_{x+y}=min(c_{x+y},max(c_y+x imes p-sum_l,c_x)))

    然后显然我们这样不一定会得到到最优解,但是这个限制比最优解要求的限制更松,同时一定能够得到最优解。

    基于这样的考量,我们希望计算 (x+y) 相同的对子中最小的 (max(c_x,c_y+x imes p-sum_l))

    事实上,(c_x) 是单调的,(c_y) 是单调的,我们的决策必然是扫到一个 (x) 使得 (c_y+x imes p-sum_l) 小于他,以及严格大于他,更新两次答案。

    这样,当 (c_x) 增大的过程,我们只需要证明相应的 (y) 会单调,即 (c_{y}-c_{y-1}ge p)(感性上看着挺对的)

    这样就可以通过双指针来合并 (c) 数组了,复杂度 (mathcal O(qlog^2 n+nlog n))

    • 可以考虑 (x+y=k) 合并了一次答案,此时 ((x+1)+(y-1)) 如何无法对答案产生贡献。

    (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 vi vector<int>
    #define ls(x) (x << 1)
    #define rs(x) (x << 1 | 1)
    #define pb push_back
    #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 ; 
    const int inf = 1e18 + 7 ; 
    int n, m, p, a[N], S ; 
    struct Tr {
    	int sum, len ; vi f ; 
    } tr[N << 2] ; 
    void pushup(int x) {
    	vi l = tr[ls(x)].f, r = tr[rs(x)].f ;
    	vi c ; c.resize(tr[x].len + 1) ;
    	rep( i, 0, tr[x].len ) c[i] = inf ; int j = 0 ; 
    	for(int i = 0; i < l.size(); ++ i) {
    		int d = i * p - tr[ls(x)].sum ; if(j) -- j ; 
    		for(; j < r.size(); ++ j) {
    			int res = max( l[i], r[j] + d ) ;
    			c[i + j] = min( c[i + j], res ) ; 
    			if( i != (l.size() - 1) ) {
    				int mx = l[i + 1] - d ; 
    				if( mx <= r[j] ) { if(j) -- j ; break ; }
    			}
    		}
    	} tr[x].f = c, tr[x].sum = tr[ls(x)].sum + tr[rs(x)].sum ; 
    }
    void build(int x, int l, int r) {
    	tr[x].len = r - l + 1 ; 
    	if( l == r ) { tr[x].sum = a[l], tr[x].f.pb(-inf), tr[x].f.pb(p - a[l]) ; return ; }
    	int mid = (l + r) >> 1 ; 
    	build(ls(x), l, mid), build(rs(x), mid + 1, r), pushup(x) ; 
    }
    void query(int x, int l, int r, int ql, int qr) {
    	if( ql <= l && r <= qr ) {
    		int res = upper_bound(tr[x].f.begin(), tr[x].f.end(), S) - tr[x].f.begin() - 1 ;
    		S = S + tr[x].sum - res * p ; return ; 
    	}
    	if( qr < l || ql > r ) return ; 
    	int mid = (l + r) >> 1 ; 
    	query(ls(x), l, mid, ql, qr), query(rs(x), mid + 1, r, ql, qr) ; 
    }
    void Q(int l, int r) {
    	S = 0, query(1, 1, n, l, r), printf("%lld
    ", S ) ; 
    }
    signed main()
    {
    	n = gi(), m = gi(), p = gi() ; 
    	rep( i, 1, n ) a[i] = gi() ; 
    	build(1, 1, n) ; int l, r ; 
    	while( m -- ) l = gi(), r = gi(), Q(l, r) ;
    	return 0 ;
    }
    
  • 相关阅读:
    Memcached 测试
    Task WaitAll的用法
    Linux Memcached 安装
    开发常用的一些插件
    转:浅析VO、DTO、DO、PO的概念、区别和用处
    转:数据库的快照隔离级别(Snapshot Isolation)
    转:nolock的替代方案-提交读快照隔离[行版本控制]
    转:介绍一个好用的抓取dump的工具-ProcDump
    把sqlserver查询结果复制到Excel出现数据记录遗漏
    ASP.NET_SessionId 不相同导致验证码出错
  • 原文地址:https://www.cnblogs.com/Soulist/p/13793113.html
Copyright © 2011-2022 走看看