[20220210联考] 传染

前言

先膜卷爷

题目

可以去卷爷那里看,或者看我的,注意时限是 \(3s\)

希尔科试图用微光控制祖安,祖安的地图可以视为一棵树,根据交通的便利程度,祖安的每个小城市有一个传播半径 \(r_i\),一旦微光弥散到此城市,那么微光也会扩散到与它距离不超过 \(r_i\) 的城市。

当然我们会给出 \(n-1\) 条路径及其长度 \(d_i\),希尔科想知道自己最少用微光控制多少城市便可以控制整个祖安。

因为金克斯忙着炸皮城,所以这个任务就交给你了。

\(1\le n\le 3\times 10^5;0\le r_i,d_i\le 10^9.\)

讲解

我们把微光从一个城市扩散至另一个城市称为感染

我们可以将问题转化为将每个点与它能够感染的城市连边,然后缩点,入度为 \(0\) 的点的个数即为答案,但很可惜,这样做时间空间都无法承受。

新技巧:点分治优化建边

我们对点分治的关键点,把它子树所有点都取出来,拷贝为两份,一份按照它与关键点的距离 \(dis\) 排序,一份按照传播半径减距离 \(rdis\) 排序。

我们接下来要做的就是将 \(rdis\ge dis\) 的点进行连边。

考虑对每一个 \(rdis_i\) 都建一个虚点 \(N_i\),然后将 \(N_i\)\(N_{i-1}\) 连边,\(N_i\) 与它能到但 \(N_{i-1}\) 不能到的点连边,最后 \(rdis_i\) 对应的点连向 \(N_i\) 即可完成建图优化,思路不难理解,代码实现也比较简单。

总之经过这么神奇的优化之后,点数变为 \(n\log_2n\) 级别,边数大概是 \(3n\log_2 n\) 级别

最后跑 Tarjan 缩点即可。

因为有排序,所以时间复杂度 \(O(n\log_2^2n)\),当然你也可以用基排,但是没有必要。

代码

//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std; 

typedef long long LL;
const int MAXN = 300005;
int n,N,ans;
int r[MAXN],deg[MAXN*25];

LL Read()
{
	LL x = 0,f = 1;char c = getchar();
	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
TT void Put1(T x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}

int head[MAXN],Head[MAXN*25],tot[2];
struct edge
{
	int v,w,nxt;
}e[MAXN<<1];
struct Edge
{
	int v,nxt;
}E[MAXN<<6];
void Add_Edge(int x,int y,int z = -1)
{
//	if(z < 0) printf("%d %d\n",x,y);
	if(z >= 0) e[++tot[0]] = edge{y,z,head[x]},head[x] = tot[0];
	else E[++tot[1]] = Edge{y,Head[x]},Head[x] = tot[1];
}
void Add_Double_Edge(int x,int y,int z = -1)
{
	Add_Edge(x,y,z);
	Add_Edge(y,x,z);
}

bool vis[MAXN];
int rt,siz[MAXN],MAX[MAXN];
void getrt(int x,int fa,int S)
{
	siz[x] = 1; MAX[x] = 0;
	for(int i = head[x],v; i ;i = e[i].nxt)
		if(!vis[v = e[i].v] && (v ^ fa))
		{
			getrt(v,x,S);
			siz[x] += siz[v];
			MAX[x] = Max(MAX[x],siz[v]);
		}
	MAX[x] = Max(MAX[x],S-siz[x]);
	if(!rt || MAX[x] < MAX[rt]) rt = x;
}
void getsiz(int x,int fa)
{
	siz[x] = 1;
	for(int i = head[x],v; i ;i = e[i].nxt)
		if(!vis[v = e[i].v] && (v ^ fa))
		{
			getsiz(v,x);
			siz[x] += siz[v];
		}
}
int cnt1,cnt2;
struct node
{
	int x,d;
	bool operator < (const node &px)const{
		return d < px.d;
	}
}dis[MAXN],rdis[MAXN];//dis & r-dis
void getdis(int x,int fa,int d)
{
	if(d > 1000000000) return;
	dis[++cnt1] = node{x,d};
	if(r[x] >= d) rdis[++cnt2] = node{x,r[x]-d};
	for(int i = head[x],v; i ;i = e[i].nxt)
		if(((v = e[i].v) ^ fa) && !vis[v])
			getdis(v,x,d+e[i].w);
}
void solve(int x)
{
	dis[cnt1 = 1] = node{x,0}; rdis[cnt2 = 1] = node{x,r[x]};
	for(int i = head[x],v; i ;i = e[i].nxt)
		if(!vis[v = e[i].v])
			getdis(v,x,e[i].w);
	sort(dis+1,dis+cnt1+1);
	sort(rdis+1,rdis+cnt2+1);
	int now = 1,lst = 0;
	for(int i = 1;i <= cnt2;++ i)
	{
		++N;
		while(now <= cnt1 && rdis[i].d >= dis[now].d) Add_Edge(N,dis[now++].x);
		if(lst) Add_Edge(N,lst);
		Add_Edge(rdis[i].x,lst = N);
	}
}
void dfs(int x)
{
	vis[x] = 1;
	getsiz(x,0);
	solve(x);
	for(int i = head[x],v; i ;i = e[i].nxt)
		if(!vis[v = e[i].v])
		{
			rt = 0; getrt(v,x,siz[v]);
			dfs(rt);
		}
}
int dfn[MAXN*25],low[MAXN*25],s[MAXN*25],tl,bl[MAXN*25],dfntot,qlt;
bool ins[MAXN*25];
void Tarjan(int x)
{
	dfn[x] = low[x] = ++dfntot; ins[s[++tl] = x] = 1;
	int v;
	for(int i = Head[x]; i ;i = E[i].nxt)
	{
		v = E[i].v;
		if(!dfn[v]) Tarjan(v),low[x] = Min(low[x],low[v]);
		else if(ins[v]) low[x] = Min(low[x],dfn[v]);
	}
	if(dfn[x] == low[x])
	{
		++qlt;
		do
		{
			ins[v = s[tl--]] = 0;
			bl[v] = qlt;
		}while(v ^ x);
	}
}

int main()
{
//	freopen("infect.in","r",stdin);
//	freopen("infect.out","w",stdout);
	N = n = Read();
	for(int i = 1;i <= n;++ i) r[i] = Read();
	for(int i = 1,u,v;i < n;++ i) u = Read(),v = Read(),Add_Double_Edge(u,v,Read());
	getrt(1,0,n); dfs(rt);
	for(int i = 1;i <= N;++ i)
		if(!dfn[i]) Tarjan(i);
	for(int x = 1;x <= N;++ x)
		for(int i = Head[x]; i ;i = E[i].nxt)
			if(bl[x] ^ bl[E[i].v])
				++deg[bl[E[i].v]];
	for(int i = 1;i <= qlt;++ i) if(!deg[i]) ++ans;
	Put(ans,'\n');
	return 0;
}

后记

其实我有个贪心的做法, 可惜过不了大样例,就不说了。

posted @ 2022-02-11 15:02  皮皮刘  阅读(69)  评论(0编辑  收藏  举报