题目
题目背景(这题心态给我搞崩了;写个背景放松一下)
回看我的博客,里面净是
D
D
(
X
Y
X
)
\sf DD(XYX)
DD(XYX) 。我便感到很悲哀了:分明说,历史是群众书写的,但是成为传奇的,只有英雄……
今天,当我再一次准备膜拜 D D ( X Y X ) \sf DD(XYX) DD(XYX) 时,他的脸色阴沉了下来。“弱者报团取暖,强者渴望孤独! O n e I n D a r k \sf OneInDark OneInDark,你天天抱团取暖,把我们的温度都给带坏了!” 只见他的双脚慢慢离开地面,他的身体飘向空中,“感受孤独吧!思考孤独吧!接受孤独吧!理解孤独吧!” 背着阳光,我眯眼看向他,一双 标杆眼 在空中泛起紫光——
“现在开始(口口有泥)!让世界感受孤独(谁该哟阔度国呦)!”
此时的空气静止如同凝固的水银。
“srand(1)
反解(辛辣天塞)!”
题目描述
很不幸的是,srand(1)
反解是另一道题。😕
给出一棵树。你可以花费 1 1 1 的代价,标记一个点。当任何一个点 x x x 被标记后(即使不是你直接操作的点),到其距离不超过 r x r_x rx 的点也会被标记。求出标记所有点的最小代价。
数据范围与提示
n
⩽
3
×
1
0
5
n\leqslant 3\times 10^5
n⩽3×105,时限
3
s
\rm 3s
3s 。
思路
a a a 被标记会导致 b b b 被标记,则连边 a → b a\to b a→b,转化为图论问题,求入度为 0 0 0 的 s c c \rm scc scc 数量。
怎样优化建图?与树上路径有关,要想到 点分树 啊。当然本题不用建出显式点分树。
时间复杂度 O ( n log 2 n ) \mathcal O(n\log^2 n) O(nlog2n) 。
代码
如果有虚点的入度为 0 0 0,会出问题。小心避免之。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <vector>
#include <cassert>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
int a = 0, c = getchar(), f = 1;
for(; !isdigit(c); c=getchar())
if(c == '-') f = -f;
for(; isdigit(c); c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
void writeint(const int &x){
if(x > 9) writeint(x/10);
putchar((x%10)^48);
}
inline void getMin(int &x, const int &y){
if(y < x) x = y;
}
inline void getMax(int &x, const int &y){
if(x < y) x = y;
}
const int MAXN = 6000005;
struct Edge{
int to, nxt, val;
Edge() = default;
Edge(int _t, int _n, int _v):
to(_t), nxt(_n), val(_v){ }
};
Edge e[MAXN<<1];
int head[MAXN], cntEdge;
void addEdge(int a, int b, int c = 0){
e[cntEdge] = Edge(b,head[a],c), head[a] = cntEdge ++;
}
# define _go(i,x) for(int i=head[x]; ~i; i=e[i].nxt)
bool vis[MAXN]; int siz[MAXN], whole;
void scan(int &rt, int x, int pre = -1){
siz[x] = 1; int mx = 0;
_go(i,x) if((i^1) != pre && !vis[e[i].to]){
scan(rt,e[i].to,i), siz[x] += siz[e[i].to];
getMax(mx,siz[e[i].to]);
}
if(mx<<1 <= whole && whole <= siz[x]<<1) rt = x;
}
const int INF = 0x3fffffff;
int dep[MAXN], sta[MAXN], top;
void collect(int x, int pre = -1){
if(dep[x] >= INF) return ; else sta[++ top] = x;
_go(i,x) if((i^1) != pre && !vis[e[i].to])
dep[e[i].to] = dep[x]+e[i].val, collect(e[i].to,i);
}
int r[MAXN], tmp[MAXN], cntNode; vector<int> G[MAXN];
bool cmp1(const int &x, const int &y){ return r[x]-dep[x] < r[y]-dep[y]; }
bool cmp2(const int &x, const int &y){ return dep[x] < dep[y]; }
void solve(int x){
scan(x,x); vis[x] = true;
top = dep[x] = 0; collect(x); memcpy(tmp+1,sta+1,top<<2);
sort(sta+1,sta+top+1,cmp1), sort(tmp+1,tmp+top+1,cmp2);
int *i = sta+1, *end_i = sta+top+1;
for(int j=0,j_=0,lst=0; i!=end_i; ++i){
while(j != top && dep[tmp[j+1]] <= r[*i]-dep[*i])
++ j, G[cntNode+1].push_back(tmp[j]);
if(j != j_){ // having different effects
++ cntNode; if(lst) G[cntNode].push_back(lst);
lst = cntNode; G[*i].push_back(lst); j_ = j;
}
else if(lst) G[*i].push_back(lst);
}
const int now_whole = whole;
_go(j,x) if(!vis[e[j].to]){
whole = siz[e[j].to] > siz[x] ? now_whole-siz[x] : siz[e[j].to];
solve(e[j].to); // just recurse
}
}
int bel[MAXN], dfn[MAXN], low[MAXN], tim;
bool inSta[MAXN]; extern int sta[MAXN], top;
void tarjan(int x){
dfn[x] = low[x] = ++ tim;
inSta[x] = true, sta[++ top] = x;
for(const int &y : G[x])
if(!dfn[y]) tarjan(y), getMin(low[x],low[y]);
else if(inSta[y]) getMin(low[x],dfn[y]);
if(low[x] == dfn[x]) for(; sta[top+1]!=x; --top)
bel[sta[top]] = x, inSta[sta[top]] = false;
}
bool dead[MAXN];
int main(){
int n = readint();
memset(head+1,-1,n<<2);
rep(i,1,n) r[i] = readint();
for(int i=1,a,b,c; i!=n; ++i){
a = readint(), b = readint(), c = readint();
addEdge(a,b,c), addEdge(b,a,c);
}
whole = cntNode = n; solve(1); top = 0;
memset(sta+1,0,(n+1)<<2); // needed!
rep(i,1,cntNode) if(!dfn[i]) tarjan(i);
rep(i,1,cntNode) for(const int &j : G[i])
if(bel[i] != bel[j]) dead[bel[j]] = true;
int ans = 0;
rep(i,1,cntNode) if(!dead[bel[i]])
++ ans, dead[bel[i]] = true;
printf("%d\n",ans);
return 0;
}
后记
就这么个玩意儿,我因为最初想错了(步了 H a n d I n D e v i l \sf HandInDevil HandInDevil 的后尘 😢)打了份错误代码调试了一下午;发现的时候,心态直接爆炸。去操场走了走,发现有个认识的在打球;随便投了两颗球,心情稍微好一点,但是困意依旧。
改成正确做法,代码又小错误不断;包括试图将新图存在旧图的 edge
里面,然后写了一堆 💩 出来……感觉整个人都浑浑噩噩……
希望天堂没有 d e b u g \rm debug debug 。