起初每两个相邻蓄水池之间都有一个隔板,而现在小苯希望在蓄水场维护一些操作和查询,具体的:
(如果挡板被拿走,则原本挡板两侧水会混合在一起,最终对应的水量会变为平均值,具体可以看样例解释。)
请你帮他回答每次查询吧。
(注意:因本题输入输出数据量较大,请选手选择快速的输入输出方式。)
输入包含行。
第一行输入两个正整数,分别表示蓄水场中水池的个数,以及操作的次数。
第二行个整数
,表示每个水池初始时的水量。
接下来行,每行第一个整数
表示操作。
如果
,则表示取掉挡板操作,再输入两个正整数
。
如果
,则表示查询操作,再输入一个正整数
。
(注意:已经拿走的挡板在以后的操作中都是“被拿走”的状态。)(数据保证至少有一次查询操作。)
输出包含若干行,每行一个实数,表示对每个的询问做出的回答。
(与正确答案的绝对误差不超过则视为正确。)
4 6 1 2 4 5 1 1 3 2 1 2 3 1 1 4 2 1 2 4
2.3333333333 2.3333333333 3.0000000000 3.0000000000
打开 1 到 3 号之间所有的挡板,则 1 2 3 号水池中的水会混合,水量为他们的平均值:7/3=2.333...。再打开 1 到 4 号之间所有的挡板,则 1 2 3 4 号水池中的水都会混合,水量为他们的平均值:12/4 = 3。
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const ll N=2e5+5;
struct UF{
double v;
UF* root;
ll rank=0;
ll cnt=1;
};
UF a[N];
ll nxt[N];
UF *Root(UF* a){
if(a->root!=a){
a->root=Root(a->root);
}
return a->root;
}
void merge(ll x,ll y){
UF* rx=Root(&a[x]);
UF* ry=Root(&a[y]);
if(rx==ry)return;
if(rx->rank<ry->rank){
rx->root=ry;
ry->cnt+=rx->cnt;
ry->v+=rx->v;
}else{
ry->root=rx;
rx->cnt+=ry->cnt;
rx->v+=ry->v;
if(rx->rank==ry->rank){
rx->rank++;
}
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); // 加速IO
ll n,m;
cin>>n>>m;
for(ll i=1;i<=n;i++){
cin>>a[i].v;
a[i].root=&a[i]; // 并查集初始化:根为自身
nxt[i] = i + 1; // 跳跃指针初始化:单个节点的右边界下一位是i+1
}
for(ll i=1;i<=m;i++){
ll op;
cin>>op;
if(op==1){
ll l,r;
cin>>l>>r;
ll cur = l;
// 区间合并:跳跃指针跳过整个连通块,避免逐点遍历
while(cur <= r){
ll block_r = nxt[cur]; // 当前连通块的右边界下一位
if(block_r > r) break; // 连通块超出目标区间,终止
merge(cur, block_r); // 合并当前块与下一个块
nxt[cur] = nxt[block_r]; // 更新跳跃指针,指向合并后新块的右边界
cur = block_r; // 处理下一个连通块
}
}else if(op==2){
ll k;
cin>>k;
printf("%.9lf\n",Root(&a[k])->v/(double)Root(&a[k])->cnt); // 输出连通块平均值
}
}
return 0;
}