NOIP模拟 graph(启发式合并)

额呵呵呵

【题目分析】

我选择死亡。。。。。。。。我的天哪ldx大佬竟然吊打了标程?究竟是。。还是。。

QAQ dalao的博客

蒟蒻只能暴力枚举拿着30分滚粗。。。点分治是什么?完全没想到好吗。。。。。

首先最后能做出贡献的边一定是在最小生成树上,这个应该不需要证吧。。。。。

然后考虑在加边的过程中,连接两个联通块能产生的贡献就是两个联通块内满足限制的点对数*边权,所以计算还是很容易的。

然后考虑如何去寻找一个点与其满足限制的点对。这里选择主席树,每次只要查找比col[i]-L小的与col[i]+L大的即可。

求完贡献后考虑合并两个联通块的信息,直接启发式合并即可,将size小的直接往size大的联通块加就行了。

(一点小建议:不要随便全局long long!容易TLE!)

【代码~】

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN=2e5+10;
const int MAXM=1e6+10;

int n,m,cnt,tot,k,maxx;
int col[MAXN],siz[MAXN],fa[MAXN],rt[MAXN];

struct Edge{
	int from,to,w;
	friend inline bool operator<(const Edge &a,const Edge &b){
		return a.w<b.w;
	}
}graph[MAXM],edge[MAXM];

struct Tree{
	int l,r;
	int sum;
}tr[MAXN*50];

struct node{
	int fa,color;
	friend inline bool operator<(const node &a,const node &b){
		if(a.fa==b.fa)
		  return a.color<b.color;
		return a.fa<b.fa;
	}
};
vector<node> vec[MAXN];

inline int Read()
{
	int i=0,f=1;
	char c;
	for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
	if(c=='-')
	  f=-1,c=getchar();
	for(;c>='0'&&c<='9';c=getchar())
	  i=(i<<3)+(i<<1)+c-'0';
	return i*f;
}

void insert(int &root,int l,int r,int x,int key)
{
	if(!root)
	  root=++tot;
	tr[root].sum+=key;
	if(l==r)
	  return ;
	int mid=l+r>>1;
	if(x<=mid)
	  insert(tr[root].l,l,mid,x,key);
	else
	  insert(tr[root].r,mid+1,r,x,key);
}

int merge(int x,int y,int l,int r)
{
	if(!x||!y)
	  return x+y;
	int mid=l+r>>1;
	tr[x].l=merge(tr[x].l,tr[y].l,l,mid);
	tr[x].r=merge(tr[x].r,tr[y].r,mid+1,r);
	tr[x].sum+=tr[y].sum;
	return x;
}

int query(int root,int l,int r,int L,int R)
{
	if(l>R||r<L)
	  return 0;
	if(L<=l&&r<=R)
	  return tr[root].sum;
	int mid=l+r>>1;
	if(R<=mid)
	  return query(tr[root].l,l,mid,L,R);
	else
	{
		if(L>mid)
		  return query(tr[root].r,mid+1,r,L,R);
		else
		  return query(tr[root].l,l,mid,L,mid)+query(tr[root].r,mid+1,r,mid+1,R);
	}
}

inline int solve(int root,int x,int k)
{
	int s=0,t=maxx;
	return query(root,0,maxx,max(s,x-k+1),min(x+k-1,t));
}

inline int find(int x)
{
	if(x==fa[x])
	  return x;
	return fa[x]=find(fa[x]);
}

inline void kruskal()
{
	for(int i=1;i<=n;++i)
	  fa[i]=i;
	sort(graph+1,graph+m+1);
	int sum=0;
	for(int i=1;i<=m;++i)
	{
		int u=graph[i].from,v=graph[i].to;
		u=find(u),v=find(v);
		if(u==v)
		  continue;
		sum++;
		fa[v]=u;
		edge[++cnt]=graph[i];
		if(sum==n-1)
		  break;
	}
}

inline void hebing(int x,int y)
{
	int siz1=vec[x].size();
	for(int i=0;i<siz1;++i)
	  vec[y].push_back(vec[x][i]);
	rt[y]=merge(rt[y],rt[x],0,maxx);
	fa[x]=y;
	siz[y]+=siz[x];
}

int main()
{
	n=Read(),m=Read(),k=Read();
	for(int i=1;i<=n;++i)
	  col[i]=Read(),maxx=max(maxx,col[i]);
	for(int i=1;i<=m;++i)
	  graph[i].from=Read(),graph[i].to=Read(),graph[i].w=Read();
	kruskal();
	tot=n;
	for(int i=1;i<=n;++i)
	{
		fa[i]=i,siz[i]=1;
		node x;
		x.fa=i,x.color=col[i];
		vec[i].push_back(x);
		rt[i]=i;
		insert(rt[i],0,maxx,col[i],1);
	}
	LL ans=0;
	for(int i=1;i<=cnt;++i)
	{
		int u=edge[i].from,v=edge[i].to;
		int fu=find(u),fv=find(v);
		if(siz[fu]>siz[fv])
		  swap(fu,fv),swap(u,v);
		int siz1=vec[fu].size(),siz2=vec[fv].size();
		for(int j=0;j<siz1;++j)
		{
			LL tmp=(siz2-solve(rt[fv],vec[fu][j].color,k));
			ans+=edge[i].w*tmp;
		}
		hebing(fu,fv);
	}
	cout<<ans;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/g21glf/article/details/83419806