题意:给你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 ' ' #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 */