1. 程式人生 > >NOIP模擬 graph(啟發式合併)

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;
}