AT2362 [AGC012B] Splatter Painting
题意
给一个n个点m条边的无向图,有q次操作 第i次操作,给出v,d,c,把所有到点v的距离不超过d的点都染上颜色c 问最后每个点的颜色 n,
m, q, c <= 100000 d <= 10
数据范围比较大, 我们如果直接暴力dfs,一直修改颜色一定会超时,然而题目要求的是最后的颜色,正难则反,如果我们倒着来染色,就会发现最后染色的就一定是答案,所以我们只需要倒着去dfs染色,如果这个点没有被染过色就染,染过色就过,这样可以省去很大一笔开销,可以过。
还有一个重要的剪枝就是在dfs时记录该点在被涂色是距离涂色中心点v的距离r,当这个点再次被搜到时,只需判断当前的r是否大于之前记录的r,否则return。(dist数组记录的是该点被遍历的时候剩余的步数是多少,如果该点之前被遍历过,并且之前到这里的时候还有5步,而这次又走到了这个点,却只剩下了3步(3 < 5),那么我们已经没有必要继续往下走了,因为下面的能到达的点已经全部被之前染过色了并且还没之前走的远。)
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 500007, M = 5000007, INF = 0x3f3f3f3f;
int n,m ;
int head[N], ver[M], nex[M], tot;
int ans[N];
struct node{
int v, d, c;
}a[N];
int dist[N];
void add(int x, int y){
ver[tot] = y;
nex[tot] = head[x];
head[x] = tot ++ ;
}
void dfs(int x, int color, int deep){
if(!ans[x])ans[x] = color;
if(deep == 0 || dist[x] >= deep)return ;
dist[x] = deep;
//dist数组记录的是x点染过的最远的距离,那么上面这个[dist[x] >= deep]
//的剪枝的意思就是如果当前x点染过色的最远距离比当前距离长,那么就返回
for(int i = head[x]; ~i; i = nex[i]){
int y = ver[i];
dfs(y, color, deep - 1);
}
}
int main(){
scanf("%d%d", &n,&m);
memset(head, -1, sizeof head);
for(int i = 1; i <= m; ++ i){
int x, y;
scanf("%d%d",&x, &y);
add(x, y), add(y, x);
}
int q;
scanf("%d", &q);
for(int i = 1; i <= q; ++ i)
scanf("%d%d%d", &a[i].v, &a[i].d, &a[i].c);
for(int i = q; i >= 1; -- i){
dfs(a[i].v, a[i].c, a[i].d);
}
for(int i = 1; i <= n; ++ i)
printf("%d\n", ans[i]);
puts("");
return 0;
}