CF1051G Distinctification()-其他
CF1051G Distinctification()
记原序列从 \(a_{1\cdots n}\) 变成 \(s_{1\cdots n}\) 最终的答案应该是
观察得这个东西与交换的过程无关,所以我们只需要关注如何找到最终的结果。
如果存在 \(i,j\) 使得 \(a_i-a_j=1\),那么交换它们,所用的代价是 \(b_j-b_i\)。
考虑最终的序列,如果有一段满足 \(a\) 值连续,那么这一段一定可以互相交换。而分析答案的式子,知道对于一个 \(a\) 的连续段,让 \(b\) 降序应该最优。
值域段需要合并,这里用并查集。
合并的同时需要维护答案,所以使用线段树合并。
#import<stdio.h>
#import<numeric>
#import<algorithm>
using namespace std;
using ll = long long;
const int N = 4e5 + 5;
const int S = N * 25;
int n,a[N],b[N],c[N],fa[N],rt[N]; ll res; bool vis[N];
int fnd(int i){ return i == fa[i] ? i : fa[i] = fnd(fa[i]); }
int lc[S],rc[S],siz[S],tot; ll sum[S],val[S];
#define mid ((L + R) >> 1)
void psu(int i,int ls,int rs){
siz[i] = siz[ls] + siz[rs];
sum[i] = sum[ls] + sum[rs];
val[i] = val[ls] + val[rs];
val[i] += sum[ls] * siz[rs];
}
void ins(int x,int &i,int L = 1,int R = 2e5){
if(!i) i = ++tot;
if(L == R) return sum[i] = val[i] = x,void(siz[i] = 1);
x <= mid ? ins(x,lc[i],L,mid) : ins(x,rc[i],mid + 1,R);
psu(i,lc[i],rc[i]);
}
int mrg(int x,int y){
if(!x || !y) return x + y; int i = x;
lc[i] = mrg(lc[x],lc[y]);
rc[i] = mrg(rc[x],rc[y]);
(lc[i] || rc[i]) ? psu(i,lc[i],rc[i]) : psu(i,y,x);
return i;
}
ll cnt(int i){ return sum[rt[i]] * (i - 1) + val[rt[i]]; };
void upd(int x,int y){
fa[y = fnd(y)] = x = fnd(x);
res -= cnt(x) + cnt(y);
rt[x] = mrg(rt[x],rt[y]);
res += cnt(x);
}
signed main(){
scanf("%d",&n);
iota(begin(fa),end(fa),0);
for(int i = 1;i <= n;++i){
scanf("%d%d",a + i,b + i);
c[i] = fnd(a[i]);
fa[c[i]] = c[i] + 1;
ins(b[i],rt[c[i]]);
}
iota(begin(fa),end(fa),0);
ll sum = 0;
for(int i = 1;i <= n;++i){
res += cnt(c[i]);
if(vis[c[i] + 1]) upd(c[i],c[i] + 1);
if(vis[c[i] - 1]) upd(c[i] - 1,c[i]);
sum += 1LL * a[i] * b[i];
vis[c[i]] = true;
printf("%lld\n",res - sum);
}
return 0;
}
————————
记原序列从 \(a_{1\cdots n}\) 变成 \(s_{1\cdots n}\) 最终的答案应该是
观察得这个东西与交换的过程无关,所以我们只需要关注如何找到最终的结果。
如果存在 \(i,j\) 使得 \(a_i-a_j=1\),那么交换它们,所用的代价是 \(b_j-b_i\)。
考虑最终的序列,如果有一段满足 \(a\) 值连续,那么这一段一定可以互相交换。而分析答案的式子,知道对于一个 \(a\) 的连续段,让 \(b\) 降序应该最优。
值域段需要合并,这里用并查集。
合并的同时需要维护答案,所以使用线段树合并。
#import<stdio.h>
#import<numeric>
#import<algorithm>
using namespace std;
using ll = long long;
const int N = 4e5 + 5;
const int S = N * 25;
int n,a[N],b[N],c[N],fa[N],rt[N]; ll res; bool vis[N];
int fnd(int i){ return i == fa[i] ? i : fa[i] = fnd(fa[i]); }
int lc[S],rc[S],siz[S],tot; ll sum[S],val[S];
#define mid ((L + R) >> 1)
void psu(int i,int ls,int rs){
siz[i] = siz[ls] + siz[rs];
sum[i] = sum[ls] + sum[rs];
val[i] = val[ls] + val[rs];
val[i] += sum[ls] * siz[rs];
}
void ins(int x,int &i,int L = 1,int R = 2e5){
if(!i) i = ++tot;
if(L == R) return sum[i] = val[i] = x,void(siz[i] = 1);
x <= mid ? ins(x,lc[i],L,mid) : ins(x,rc[i],mid + 1,R);
psu(i,lc[i],rc[i]);
}
int mrg(int x,int y){
if(!x || !y) return x + y; int i = x;
lc[i] = mrg(lc[x],lc[y]);
rc[i] = mrg(rc[x],rc[y]);
(lc[i] || rc[i]) ? psu(i,lc[i],rc[i]) : psu(i,y,x);
return i;
}
ll cnt(int i){ return sum[rt[i]] * (i - 1) + val[rt[i]]; };
void upd(int x,int y){
fa[y = fnd(y)] = x = fnd(x);
res -= cnt(x) + cnt(y);
rt[x] = mrg(rt[x],rt[y]);
res += cnt(x);
}
signed main(){
scanf("%d",&n);
iota(begin(fa),end(fa),0);
for(int i = 1;i <= n;++i){
scanf("%d%d",a + i,b + i);
c[i] = fnd(a[i]);
fa[c[i]] = c[i] + 1;
ins(b[i],rt[c[i]]);
}
iota(begin(fa),end(fa),0);
ll sum = 0;
for(int i = 1;i <= n;++i){
res += cnt(c[i]);
if(vis[c[i] + 1]) upd(c[i],c[i] + 1);
if(vis[c[i] - 1]) upd(c[i] - 1,c[i]);
sum += 1LL * a[i] * b[i];
vis[c[i]] = true;
printf("%lld\n",res - sum);
}
return 0;
}