https://codeforces.com/contest/1515/problem/F
思路:
证明参考:https://www.luogu.com.cn/blog/_post/331276
我开始想的是,每次都选最大的ai 之后将他与跟他相连的且不在他集合中的点合并,尝试用优先队列 + 并查集可以轻松实现。
然后wa了。
发现有个很重要的问题没有处理。如果按照下面这样去处理。每次当前连通块合并了新的,但是原来之前合并到这个连通块里的,其siz并没有更新,显然一个连通块里的每个点的siz不同了。肯定有问题
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=3e5+1000;
typedef long long LL;
typedef pair<LL,LL>P;
inline LL read(){LL x=0,f=1;char ch=getchar(); while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;}
struct Edge{
LL v,id;
};
vector<Edge>g[maxn];
LL fa[maxn],siz[maxn],val[maxn];
LL find(LL x){
if(x!=fa[x]) return fa[x]=find(fa[x]);
return fa[x];
}
int main(void){
cin.tie(0);std::ios::sync_with_stdio(false);
LL n,m,x;cin>>n>>m>>x;
LL sum=0;
for(LL i=1;i<=n;i++) cin>>val[i],fa[i]=i,siz[i]=val[i],sum+=val[i];
for(LL i=1;i<=m;i++){
LL u,v;cin>>u>>v;
g[u].push_back({v,i});
g[v].push_back({u,i});
}
if(sum<(n-1)*x){cout<<"NO"<<"\n";return 0;}
priority_queue<P>que;
for(LL i=1;i<=n;i++){
que.push({val[i],i});
}
cout<<"YES"<<"\n";
vector<LL>ans;
while(que.size()>=1){
P now=que.top();que.pop();
LL va=now.first;LL u=now.second;
va=siz[find(u)];
/// cout<<"va="<<va<<" "<<"u="<<u<<"\n";
if(!g[u].empty()){
LL to=g[u].back().v;LL edgeid=g[u].back().id;
/// debug(to);
if(find(to)==find(u)){
g[u].pop_back();
}
else{
ans.push_back(edgeid);
siz[find(u)]+=siz[find(to)];
siz[find(to)]=0;
fa[find(to)]=find(u);
siz[find(u)]-=x;///本次合并花费
/// debug(siz[find(u)]);
/// debug(u);debug(to);
que.push({siz[find(u)],u});
que.push({siz[find(u)],to});
}
}
}
for(auto i:ans){
cout<<i<<"\n";
}
return 0;
}
然后我尝试将这样的点扔了,重新扔进去正确的点。那毫无疑问tle了。
LL va=now.first;LL u=now.second;
LL fac=siz[find(u)];
if(fac!=va){
que.push({fac,u});
continue;
}
正确的思路是每次找到当前正确能代表连通块siz的点,然后以它去合并,同时启发式合并处理好新连通块能直连的边。不正确的就continue掉
这样的复杂度就是O(nlognlogn)的
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=3e5+1000;
typedef long long LL;
typedef pair<LL,LL>P;
inline LL read(){LL x=0,f=1;char ch=getchar(); while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;}
struct Edge{
LL v,id;
};
vector<Edge>g[maxn];
LL fa[maxn],val[maxn];
LL a[maxn];
LL find(LL x){
if(x!=fa[x]) return fa[x]=find(fa[x]);
return fa[x];
}
int main(void){
LL n,m,x;n=read();m=read();x=read();
LL sum=0;
for(LL i=1;i<=n;i++) val[i]=read(),fa[i]=i,sum+=val[i],a[i]=val[i];
for(LL i=1;i<=m;i++){
LL u,v;u=read();v=read();
g[u].push_back({v,i});
g[v].push_back({u,i});
}
if(sum<(n-1)*x){printf("NO\n");return 0;}
priority_queue<P>que;
for(LL i=1;i<=n;i++){
que.push({val[i],i});
}
printf("YES\n");
vector<LL>ans;
for(LL i=1;i<=n-1;i++){
P now=que.top();que.pop();
LL u=now.second;
while(u!=find(u)){///跳过队列中的无效点
now=que.top();que.pop();
u=now.second;
continue;
}
while(u==find(g[u].back().v)&&!g[u].empty()) g[u].pop_back();
ans.push_back(g[u].back().id);
LL newanc=find(g[u].back().v);///合并成新的连通块
a[u]+=a[newanc]-x;
que.push({a[u],u});
fa[newanc]=find(u);
if(g[u].size()<g[newanc].size()) swap(g[u],g[newanc]);
g[u].insert(g[u].end(),g[newanc].begin(),g[newanc].end());
g[newanc].clear();
}
for(auto i:ans){
printf("%lld\n",i);
}
return 0;
}