1. 程式人生 > >JZOJ 5926. 【NOIP2018模擬10.25】naive 的圖

JZOJ 5926. 【NOIP2018模擬10.25】naive 的圖

題目

給定一個帶權無向圖。每個點都有它的顏色。定義 D ( s , t ) D(s,t) 為s到t中最短路徑中邊權最大的邊的權值。
求所有滿足 u

< v , c u c v
< L u<v,|c_u-c_v|<L D ( u
, v ) D(u,v)
之和。

關鍵字

(0)最短路徑,一定對應著MST上的一條鏈。
①拆分詢問
②動態刪邊轉化成靜態加邊
③如果暴力中點的總數不多,暴力的複雜度不會很大。

正解

顯然,不在MST上的邊是無用的。
50分做法:關鍵字③,每次合併2個連通塊,直接將連通塊大小相乘,再乘以邊權,就是這條邊對答案的貢獻了。
70分的做法:類比L=0,顏色不是很多,所以合併兩個連通塊時,暴力列舉配對的顏色即可。
顏色如何列舉?
考慮kruskal重構樹,按照邊權從小到大連邊時,方點代表邊,圓點代表原圖中的點。
弄個bfs。一個連通塊必然對應著kruskal重構樹的dfs序中連續的一段。
100分的做法:顏色不能暴力列舉,那麼考慮將哪些點作為詢問點。顯然選擇小的連通塊的點。
所以詢問點的數量不會超過O(n log n)
拆分詢問,然後詢問dfs序上一段區間的點的個數。

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 400010
#define M1 500010
#define M 6000010
#define LL long long
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct Graph{
	int next[N],to[N],head[N],tot;
	int fa[N];
	void lb(int x,int y){
		to[++tot]=y;next[tot]=head[x];head[x]=tot;
	}
	void clear(){
		tot=0;
		memset(next,0,sizeof(next));
		memset(head,0,sizeof(head));
	}
}G;
int read(){
	int fh=0,rs=0;char ch=0;
	while((ch<'0'||ch>'9')&&(ch^'-'))ch=getchar();
	if(ch=='-')fh=1,ch=getchar();
	while(ch>='0'&&ch<='9')rs=(rs<<3)+(rs<<1)+(ch^'0'),ch=getchar();
	return fh?-rs:rs;
}
struct nob{int x,y;LL z;}c[M1];
struct noq{
	int g1,g2,g3;
}qu[N];
struct noq1{
	int c,gl,gr,xs,tp;
}xw[M];
int tr[N];
int i,j,k,l,n,m,L,CNT;
int f[N],g[N],gx,gy,col[N],siz[N];
LL ans,w;
int dfn[N],tar[N],tot,wz,wz1;
bool bz[N];
int st[N],top,head1[N];
int u,v,step,opl,opr,opx,opz,rt;
int get(int x){return f[x]==x?x:f[x]=get(f[x]);}
bool cmp(nob a,nob b){return a.z<b.z;}
bool cmp1(noq1 a,noq1 b){return a.c<b.c||(a.c==b.c&&a.tp<b.tp);}
int lowbit(int x){return x&(-x);}
void add(int x,int num){
	for(;x<=n+step;x=x+lowbit(x))tr[x]=tr[x]+num;
}
int qry(int x){
	int rs=0;
	for(;x;x=x-lowbit(x))rs=rs+tr[x];
	return rs;
}
void dgG(int x){
	int i;bool p;
	st[top=1]=x;
	memcpy(head1,G.head,sizeof(head1));
	while(top){
		x=st[top];
		p=0;
		if(!bz[x]){
			bz[x]=1;
			dfn[x]=++tot;tar[tot]=x;
        	siz[x]=1;
		}
		for(i=head1[x];i;i=G.next[i])
		    if(G.fa[x]^G.to[i]){
		    	G.fa[G.to[i]]=x;
		    	p=1;
		    	head1[x]=G.next[i];
		    	st[++top]=G.to[i];
		    	break;
			}
		if(!p){
			if(top>1){
				siz[st[top-1]]=siz[st[top-1]]+siz[st[top]];
			}
			top--; 
		}
	}
}
void merge(int x,int y){
	int gx=get(x),gy=get(y);
	if(gx^gy)f[gy]=gx,g[gx]+=g[gy],g[gy]=0;
}
int main(){
	n=read(),m=read(),L=read();
	fo(i,1,n)col[i]=read();
	fo(i,1,m){
		u=read(),v=read(),w=read();
		if(u==v)continue;
		c[i].x=u,c[i].y=v,c[i].z=w;
	}
	sort(c+1,c+m+1,cmp);
	step=0;
	fo(i,1,n)f[i]=i,g[i]=1;
	fo(i,1,m){
		gx=get(c[i].x);
		gy=get(c[i].y);
		if(gx^gy){
			step++;
			f[n+step]=n+step;
			ans=ans+1ll*g[gx]*g[gy]*c[i].z;
			if(g[gx]<g[gy]){
				qu[step].g1=gx;
			    qu[step].g2=gy;
			}else{
				qu[step].g1=gy;
			    qu[step].g2=gx;
			}
			merge(n+step,gx);
			merge(n+step,gy);
			G.lb(n+step,gx);
			G.lb(n+step,gy);
			qu[step].g3=c[i].z;
			if(step==n-1)break;
		}
	}
	rt=n+step;
	dgG(rt);
	fo(i,1,step){
		u=qu[i].g1;v=qu[i].g2;w=qu[i].g3;
		fo(j,dfn[u],dfn[u]+siz[u]-1)if(tar[j]<=n&&L){
			k=tar[j];
			xw[++CNT].c=col[k]-L;
			xw[CNT].xs=-w;
			xw[CNT].gl=dfn[v];
			xw[CNT].gr=dfn[v]+siz[v]-1;
			xw[CNT].tp=1;
			xw[++CNT].c=col[k]+L-1;
			xw[CNT].xs=w;
			xw[CNT].gl=dfn[v];
			xw[CNT].gr=dfn[v]+siz[v]-1;
			xw[CNT].tp=1;
		}
	}
	fo(i,1,n){
		xw[++CNT].c=col[i];
		xw[CNT].tp=0;
		xw[CNT].gl=dfn[i];
	}
	sort(xw+1,xw+CNT+1,cmp1);
  	wz=1;wz1=1;
	for(wz<=CNT;xw[wz].c<0;wz++);
	int ii;
	while(wz<=CNT){
		if(xw[wz].tp==0){
			add(xw[wz].gl,1);
		}else{
			w=1ll*(qry(xw[wz].gr)-qry(xw[wz].gl-1));
			ans=ans-1ll*w*xw[wz].xs;
		}
		wz++;
	}
	printf("%lld",ans);
	return 0;
}