jzoj5926 naive 的图 最小生成树+启发式合并+线段树合并

Description


众所周知,小 naive 有一张 n 个点,m 条边的带权无向图。第 i 个点的颜色为 ci。d(s, t)表示从点 s 到点 t 的权值最小的路径的权值,一条路径的权值定义为路径上权值最大的边的权值。
求所有满足 u < v, |cu − cv| ≥ L 的点对 (u, v) 的 d(u, v) 之和。

Solution


最后5秒没交上去。。

第一个条件没什么用,只是为了防止算重,我们可以不管它。
显然这样的边会在最小生成树上。我们从小到大枚举边,显然一条边会连通两个连通块
考虑算这条边的贡献,我们枚举size较小的子树中的节点,查询另一子树中给定区间内c的数量
于是我们可以每个联通块开一棵线段树,每次合并连通块就线段树合并,每次线段树区间查询

算一下复杂度。由于每个连通块合并一次后size至少变为2倍,因此只会合并log次,每个元素只会被插入log^2次。线段树合并的复杂度我不会算,但是可知和元素个数有关,实测随机数据表现优良。
空间按理说应该开nlog^2的,但是出题人并没有卡这个东西。。。
感觉这样就是在倒着边分治啊

嗯,这很noip模拟

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

typedef long long LL;
const int N=200005;
const int E=500005;

struct edge {int x,y,w;} h[N],g[E];
struct treeNode {int l,r,sum;} t[N*51];
struct data {
	int fi,se;
	bool operator <(data b) const {
		return (fi!=b.fi)?(fi<b.fi):(se<b.se);
	}
} ;

std:: vector <data> vec[N];

int c[N],size[N],root[N],fa[N];
int lim,tot,n;

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

int query(int now,int tl,int tr,int l,int r) {
	if (r<l) return 0;
	if (tl>=l&&tr<=r) return t[now].sum;
	int mid=(tl+tr)>>1;
	int qx=query(t[now].l,tl,mid,l,std:: min(r,mid));
	int qy=query(t[now].r,mid+1,tr,std:: max(mid+1,l),r);
	return qx+qy;
}

void modify(int &now,int tl,int tr,int x,int v) {
	if (!now) now=++tot; t[now].sum+=v;
	if (tl==tr) return ;
	int mid=(tl+tr)>>1;
	if (x<=mid) modify(t[now].l,tl,mid,x,v);
	else modify(t[now].r,mid+1,tr,x,v);
}

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

int ask(int root,int x,int L) {
	return query(root,0,lim,std:: max(0,x-L+1),std:: min(x+L-1,lim));
}

int find(int x) {
	return !fa[x]?x:(fa[x]=find(fa[x]));
}

bool cmp(edge a,edge b) {
	return a.w<b.w;
}

int main(void) {
	freopen("data.in","r",stdin);
	// freopen("graph.out","w",stdout);
	n=read(); int m=read(),L=read(),cnt=0;
	rep(i,1,n) c[i]=read(),lim=std:: max(lim,c[i]);
	rep(i,1,m) {
		int x=read(),y=read(),w=read();
		g[i].x=x,g[i].y=y,g[i].w=w;
	}
	std:: sort(g+1,g+m+1,cmp);
	rep(i,1,m) {
		if (find(g[i].x)!=find(g[i].y)) {
			fa[find(g[i].x)]=find(g[i].y);
			h[++cnt]=g[i];
		}
	} tot=n;
	rep(i,1,n) {
		fa[i]=0,size[i]=1;
		vec[i].push_back((data) {i,c[i]});
		root[i]=i;
		modify(root[i],0,lim,c[i],1);
	}
	LL ans=0;
	rep(ti,1,cnt) {
		int x=h[ti].x,y=h[ti].y;
		int fx=find(x),fy=find(y);
		if (size[fx]>size[fy]) {
			std:: swap(fx,fy);
			std:: swap(x,y);
		}
		int szefx=vec[fx].size(),szefy=vec[fy].size();
		for (int i=0,j=0;i<szefx;++i) {
			LL tmp(szefy-ask(root[fy],vec[fx][i].se,L));
			ans+=1LL*h[ti].w*tmp;
		}
		for (int i=0;i<szefx;++i) vec[fy].push_back(vec[fx][i]);
		root[fy]=merge(root[fy],root[fx],0,lim);
		fa[fx]=fy; size[fy]+=size[fx];
	}
	printf("%lld\n", ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个简单的示例代码: 首先,需要安装 Naive UI 组件库和相关依赖: ``` npm install naive-ui -S npm install @vue/composition-api -S ``` 然后,创建一个名为 `ProvinceCityArea.vue` 的组件,代码如下: ```vue <template> <n-tree :data="data" v-model:value="selected" :accordion="false" :is-item-active="isItemActive" :render-label="renderLabel" /> </template> <script lang="ts"> import { defineComponent, ref } from '@vue/composition-api' import { NTree } from 'naive-ui' import { City, getProvinces } from '@/utils/city' export default defineComponent({ name: 'ProvinceCityArea', components: { NTree }, setup() { const data = ref<Array<any>>([]) const selected = ref<string[]>([]) const isItemActive = (item: any) => { return selected.value.includes(item.code) } const renderLabel = (item: any) => { return `${item.name} (${item.code})` } const loadData = () => { const provinces = getProvinces() data.value = provinces.map((province: any) => { const cities = City[province.code] || [] return { name: province.name, code: province.code, children: cities.map((city: any) => { const areas = City[city.code] || [] return { name: city.name, code: city.code, children: areas.map((area: any) => { return { name: area.name, code: area.code } }) } }) } }) } loadData() return { data, selected, isItemActive, renderLabel } } }) </script> ``` 上面代码中,我们通过 `n-tree` 组件来渲染省市区选择。 `data` 是用于存储形结构数据的数组,`selected` 用于存储当前选中的结点的 `code` 值。 `isItemActive` 方法用于判断某个结点是否被选中,通过判断 `selected` 数组中是否包含该结点的 `code` 值来实现。 `renderLabel` 方法用于渲染结点标签,这里我们展示的是结点的名称和 `code` 值。 `loadData` 方法用于加载数据,我们通过 `getProvinces` 方法来获取所有的省份数据,然后逐个遍历并构造形结构数据。 在 `ProvinceCityArea.vue` 组件中引入 `City` 和 `getProvinces` 方法是因为我们需要用到这些数据来构造形数据。 最后,我们在需要使用省市区选择的地方,像使用其他组件一样使用 `ProvinceCityArea` 组件即可。 ```vue <template> <province-city-area /> </template> <script lang="ts"> import { defineComponent } from '@vue/composition-api' import ProvinceCityArea from '@/components/ProvinceCityArea.vue' export default defineComponent({ name: 'App', components: { ProvinceCityArea } }) </script> ``` 以上就是使用 Naive UI 组件库中的 `n-tree` 组件生成一个省市区选择的示例代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值