1. 程式人生 > >[HDU3038] [2009多校聯考13] How Many Answers Are Wrong [帶權並查集]

[HDU3038] [2009多校聯考13] How Many Answers Are Wrong [帶權並查集]

題意:給出一個長度為NN的序列qq。 按順序給出MM條資訊,每條資訊表示區間[Ai,Bi][A_i,B_i]的和為SiSi。 如果第ii條資訊和之前已知的資訊衝突,那麼這條資訊是無效的。 求一共有多少條無效資訊。

我們考慮對第ii條資訊建立一個關係AiBiA_i \to B_i,表示j=AiBiqj\sum\limits_{j=A_i}^{B_i}q_jSiS_i 這其實是字首和的差值SumAi+Si=SumBiSum_{A_i}+S_i=Sum_{B_i} 某一個點P

P可能有很多個點有關係。這些關係不需要一一表示出來 所有和PP有關係的點顯然可以看作一個集合 就像一顆樹,用一個點XX作為這個集合的根 那麼我們只需要分別記錄XPX \to P也就是XXPP的關係,就可以表示出PP現有的所有關係。

假設現在處理到(Ai,Bi,Si)(A_i,B_i,S_i)Val(Ai)=Father(Ai)AiVal(A_i)=Father(A_i) \to A_iVal(Bi)=Father(Bi)BiVal(B_i)=Father(B_i) \to B_i

(Bi)=Father(Bi)Bi

如果AiA_iBiB_i本來就間接有關係了,那麼F=Father(Ai)=Father(Bi)F=Father(A_i)=Father(B_i) 可以利用Val(Ai)=FAiVal(A_i)=F \to A_iVal(Bi)=FBiVal(B_i)=F \to B_i來得到AiA_iBiB_i已知的關係 即AiBi=Val(Bi)Val(Ai)A_i \to B_i=Val(B_i)-Val(A_i)

iBi=Val(Bi)Val(Ai) 顯然,這條資訊是否合法可以根據Val(Bi)Val(Ai)Val(B_i)-Val(A_i)是否等於SiS_i來判定。

如果AiA_iBiB_i本來沒有關係,現在它們有關係了,Father(Ai)Father(A_i)Father(Bi)Father(B_i)也有關係了 我們就要把它們關聯起來,比方說建立Father(Bi)Father(Ai)Father(B_i) \to Father(A_i) 更新Father(Father(Ai))=Father(Bi)Father(Father(A_i))=Father(B_i) Val(Father(Ai))=Val(Bi)+SiVal(Ai)Val(Father(A_i))=Val(B_i)+S_i-Val(A_i)

以上,建一個帶權並查集就可以解決這道題

值得一提的是本題的區間實際上是離散的點,所以區間要建成左開右閉或者左閉右開

可能這麼講比較容易懂:AiBi=Sum[Bi]Sum[Ai1]A_i \to B_i = Sum[B_i] - Sum[A_i-1]

類似的例子線上段樹題裡面也有,一個經典的資料就是[1,5][1,5][6,10][6,10]這兩個區間

注意路徑壓縮的時候也要更新權值

Code:

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<cmath>
using namespace std;
int N,M;
int fa[200005]={},val[200005]={};
int find(int x)
{
    if(x==fa[x])return x;
    else
    {
        int top=find(fa[x]);
        val[x]+=val[fa[x]];
        return fa[x]=top;
    }
}
int main()
{
    while(scanf("%d%d",&N,&M)!=EOF)
    {
        for(int i=0;i<=N;++i)fa[i]=i,val[i]=0;
        int Ans=0;
        for(int ta,tb,tc,ffa,ffb,i=1;i<=M;++i)
        {
            scanf("%d%d%d",&ta,&tb,&tc);
            --ta;
            ffa=find(ta),ffb=find(tb);
            if(ffa!=ffb)
            {
                fa[ffb]=ffa;
                val[ffb]=val[ta]-val[tb]+tc;
            }
            else if(val[tb]-val[ta]!=tc)++Ans;
        }
        printf("%d\n",Ans);
    
    }
    return 0;
}