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]这一段中。所以可以用区间修改来做。

浙公网安备 33010602011771号