小笨的蓄水池

归游發表於2024-11-18

題目描述

小苯有 ( n ) 個水池,編號從 1 到 ( n ),每個水池中有一定的水量 ( \(a_i\) )。水池之間有隔板,初始狀態下這些隔板將水池隔開。

小苯需要進行兩種操作:

移除隔板:操作格式為 1 l r,表示將第 ( l ) 個水池和第 ( r ) 個水池之間的所有隔板移除。這意味著在這個範圍內的水池將會合並,水量會變成這些水池水量的平均值。

查詢水量:操作格式為 2 i,表示查詢第 ( i ) 個水池當前的水量。如果該水池的隔板已經被移除,它的水量將是合併後的平均值。

因此,題目的核心在於處理水池之間的隔板移除和水量查詢,確保在移除隔板後能夠正確計算和返回水量。

思路

這種資料的處理,沒有尋常的資料結構處理,但是,這種區間合併的操作,與並查集非常類似

並查集的思想在於,兩顆樹之間的合併,而這裡的區間亦可以進行類似的操作

移除隔板後,就將相鄰的區間合併(合併相鄰兩顆子樹),查詢時就查詢區間被合併到哪個區間了(類似於父節點)

CODE

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
struct node{
    int l,r,cnt;
    double sum;
}a[maxn];
int f[maxn];
int n,m;
int find(int x){
    if(x!=f[x]) return f[x]=find(f[x]);
    return x;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i){
        scanf("%lf",&a[i].sum);
        a[i].l=a[i].r=i;
        a[i].cnt=1;f[i]=i;
    }
    while(m--){
        int op;scanf("%d",&op);
        if(op==1){
            int x,y;scanf("%d %d",&x,&y);
            double ans=0;
            for(int i=a[f[x]].r+1;i<=a[f[y]].l;++i){
                if(find(x)==find(i)) continue;
                int fx=find(x),fi=find(i);
                f[fi]=fx;
                a[fx].sum+=a[fi].sum;
                a[fx].cnt+=a[fi].cnt;
                a[fx].l=min(a[fx].l,a[fi].l);
                a[fx].r=max(a[fx].r,a[fi].r);
            }
        }
        else{
            int x;scanf("%d",&x);
            printf("%.12f\n",a[find(x)].sum*1.0/a[find(x)].cnt);
        }
    }
    return 0;    
}

相關文章