題意:給你n個點,m次操作。操作1是合並a和b;操作2是在p及其連通塊內所有點上增加大小為t的值。為你m次操作之后,各個點的大小是多少?
評測用例及其得分:
對於 30% 的評測用例,1 ≤ n ≤ 20,1 ≤ m ≤ 100。
對於 50% 的評測用例,1 ≤ n ≤ 100,1 ≤ m ≤ 1000。
對於 70% 的評測用例,1 ≤ n ≤ 1000,1 ≤ m ≤ 10000。
對於所有評測用例,1 ≤ n ≤ 10000,1 ≤ m ≤ 100000,1 ≤ t ≤ 100。
個人想說的話:本來是不想寫題解的,因為沒必要,但是網上好像真沒有人寫出滿分正解目前為止,那我就寫寫吧。
解法:看到一道題就需要分析復雜度,做題千萬不要無腦去莽,很多人上來就是一個add邊+bfs暴搜修改權值,這當然可以,但是你只能得70%的分。(最壞復雜度:先1000次操作1建邊,最后建到1000個點的雙聯通圖;再9000次操作2bfs,復雜度是O(9000*1000+1000),大概是O(9e6)的復雜度,能過70%樣例。)
但是這種做法在ACM賽制里還是TLE。
所以我們需要考慮優化,看到這種合並題,第一反應應該就是dsu啊?那我們考慮怎么搞並查集,普通並查集顯然8太行(或者維護起來很麻煩),觀察性質可以知道,我們只需要維護當前點p及其當前點p的root的差值就可以了。那就好辦了啊,帶權並查集直接上啊,所以每個點最后的結果就是它與它祖先root的差值+root自身的值就可以了。
我們對於操作1,那就是非連通塊的話合並並且更新其d值,已經是連通塊就continue掉。
我們對於操作2,其實意思就是在他祖先點進行修改就可以了,因為我們要求的最終結果是祖先自己的值+該點與祖先的差值。
不知道你們懂了沒qwq,附上我自己寫的代碼,歡迎Hack~

/* Author: Anonytt Date: 08/08/20 20:35 */ #include<bits/stdc++.h> #pragma GCC optimize(2) #define ll long long #define rep(i,a,n) for(int i=a;i<=n;i++) #define per(i,n,a) for(int i=n;i>=a;i--) #define endl '\n' #define eps 0.000000001 #define pb push_back #define mem(a,b) memset(a,b,sizeof(a)) #define IO ios::sync_with_stdio(false);cin.tie(0); using namespace std; const int INF=0x3f3f3f3f; const ll inf=0x3f3f3f3f3f3f3f3f; const int mod=1e9+7; const int maxn=1e4+5; int fa[maxn],d[maxn],now[maxn],n,m; int find(int x){ if(x!=fa[x]){ int temp=fa[x]; fa[x]=find(fa[x]); d[x]+=d[temp]; } return fa[x]; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) fa[i]=i; while(m--){ int op;scanf("%d",&op); if(op==1){ int a,b;scanf("%d%d",&a,&b); int eu=find(a),ev=find(b); if(eu!=ev){ fa[ev]=eu; d[ev]=now[ev]-now[eu]; } } else{ int p,t;scanf("%d%d",&p,&t); int rt=find(p); now[rt]+=t; } } rep(i,1,n){ printf("%d ",now[find(i)]+d[i]); } puts(""); } /* 4 8 1 1 2 2 1 10 2 3 5 1 4 1 2 2 2 1 1 2 1 2 4 2 2 1 */