D. Reverse Sort Sum_线段树维护差分

D. Reverse Sort Sum

题目大意:

有一个01串A。

定义f(k,A):将A的前k个数非降序排序所得到的串。现在有Bi=f(i,A)。现在有数组C,其定义如下:
$$
C_j=\sum_{i = 1}^{i = n}B_{i,j}
$$
现在给出串C,要求回推初始串A。

思路和代码:

首先可以发现将C里的元素累加起来除n可以得到一共有几个1。

然后发现若最后一个元素等于其位置编号则其在A中一定是1。若这个是1,并且i及i之前一定是排好序的,所以所有的1都在i前面紧密排列。那么我们就可以直接将i之前(包括i)的一个区间整体减去1。就是说,区间修改+单点查询,可以用差分树状数组,可以用懒标记,可以用差分线段树。这里我写一个我比较熟的差分线段树。

ll p[N] ;

struct Node{
	int l , r ;
	ll val ;
}tr[N << 2];

void pushup(Node &f , Node &l , Node &r){
	f.val = l.val + r.val ;
}

void build(int now , int l , int r){ 
	if(l == r){
		tr[now] = {l , r , p[l]} ;
		return ;
	}
	tr[now] = {l , r , 0 };
	int mid = l + r >> 1 ;
	build(now << 1 , l , mid) ;
	build(now << 1 | 1 , mid + 1 , r) ;
	pushup(tr[now] , tr[now << 1] , tr[now << 1 | 1]) ;
}

ll query(int now , int l , int r){ 
	//差分数组单点查询即前缀和查询
	if(l <= tr[now].l && tr[now].r <= r) return tr[now].val ;
	int mid = tr[now].l + tr[now].r >> 1 ;
	ll res = 0LL  ;
	if(l <= mid) res += query(now << 1 , l , r) ;
	if(mid < r ) res += query(now << 1 | 1 , l , r) ;
	return res ;
}

void modify(int now , int x , ll det){
	if(tr[now].l == tr[now].r && tr[now].l == x){
		tr[now].val += det ; return ;
	}
	int mid = tr[now].l + tr[now].r >> 1 ;
	if(x <= mid) modify(now << 1 , x , det) ;
	else modify(now << 1 | 1 , x , det) ;
	pushup(tr[now] , tr[now << 1] , tr[now << 1 | 1]) ;
}

void solve(){
	cin >> n ;
	vct<ll> a(n + 1 , 0) ; 
	vct<ll> ans(n + 1 , 0) ; 
	
	rep(i , 1 , n) cin >> a[i] ;
	
	rep(i , 1 , n) p[i] = a[i] - a[i - 1] ;
	
	build(1 , 1 , n) ;
	
	ll k = accumulate(a.begin() + 1 , a.end() , 0LL) / (1LL * n) ;
	
	drep(i , 1 , n){
		int pre = i - k + 1 ;
		ll now = query(1 , 1 , i) ;
		if(!now) continue ;
		if(now == i){
			ans[i] = 1 ;
			k -- ;
		}
		
		modify(1 , pre , -1) ;
		if(i + 1 <= n) modify(1 , i + 1 , 1) ;
		
	}
	
	rep(i , 1 , n) cout << ans[i] << " \n"[i == n] ;
	
}//code_by_tyrii 
小结:

这题的关键就是f(k,A)的规律。即在[1,k]区间中是[00...111]这样的。就是说当前剩余的1的数量对整体的影响的只集中于[i-k+1,i]这一段中。所以可以用区间修改来做。

posted @ 2022-04-19 14:38  tyrii  阅读(65)  评论(0)    收藏  举报