P5852 [USACO19DEC]Bessie's Snow Cow P 题解
题意分析
给定一棵树,定义树上一个点的权值为它染的不同颜色的个数(一个点会有多种颜色)
你需要支持两种操作:
- 将\(x\)的子树全部染上(添加)某种颜色
- 查询给定\(x\)的子树内的所有点的权值之和
考场思路
对于每一个点直接使用\(set\)记录其染上的颜色,并在修改时记录每一个点的子树权值后缀和,这样可以做到\(O(n\log n)\)的单次修改以及\(O(1)\)的查询,但是考虑到存在\(Q(1\leq Q \leq 10^5)\)次操作,最坏复杂度就是\(O(Qn\log n)\)(不断进行修改操作),肯定会炸。
考场代码
#include<iostream>
#include<cstring>
#include<vector>
#include<set>
using namespace std ;
namespace IO
{
const size_t SIZE = 1 << 16 ;
#define isdigit(ch) (ch >= '0' && ch <= '9')
namespace Read
{
char buf[SIZE] , *_now = buf , *_end = buf ;
char getchar()
{
if(_now == _end)
{
_now = _end = buf ;
_end += fread(buf , 1 , SIZE , stdin) ;
if(_now == _end)
return EOF ;
}
return *(_now++) ;
}
template<typename T>
void read(T& w)
{
w = 0 ;
short f = 1 ;
char ch = getchar() ;
while(!isdigit(ch))
{
if(ch == '-')
f = -1 ;
ch = getchar() ;
}
while(isdigit(ch))
w = (w << 1) + (w << 3) + (ch ^ 48) , ch = getchar() ;
w *= f ;
}
void read(char& ch)
{
char tmp = getchar() ;
while(tmp == ' ' || tmp == '\n')
tmp = getchar() ;
ch = tmp ;
}
void read(char* s)
{
char ch = getchar() ;
int len = 0 ;
while((ch == ' ' || ch == '\n') && ch != EOF)
ch = getchar() ;
while(ch != ' ' && ch != '\n' && ch != EOF)
s[len++] = ch , ch = getchar() ;
s[len] = '\0' ;
}
class qistream
{
public:
template<typename T>
qistream& operator>>(T& w)
{
read(w) ;
return *this ;
}
qistream& operator>>(char* s)
{
read(s) ;
return *this ;
}
} qcin ;
}
namespace Write
{
char buf[SIZE] , *p = buf ;
void flush()
{
fwrite(buf , 1 , p - buf , stdout) ;
p = buf ;
}
void putchar(char ch)
{
if(p == buf + SIZE)
flush() ;
*p = ch ;
++p ;
}
class Flush{public:~Flush(){flush() ;} ;}_;
template<typename T>
void write(T x)
{
char st[50] ;
int len = 0 ;
if(x < 0)
putchar('-') , x = -x ;
do
{
st[++len] = x % 10 + '0' ;
x /= 10 ;
} while(x) ;
while(len)
putchar(st[len--]) ;
}
void write(const char* s)
{
int len = strlen(s) ;
for(int i = 0 ; i < len ; ++i)
putchar(s[i]) ;
}
void write(char* s)
{
int len = strlen(s) ;
for(int i = 0 ; i < len ; ++i)
putchar(s[i]) ;
}
void write(char ch)
{
putchar(ch) ;
}
class qostream
{
public:
template<typename T>
qostream& operator<<(T x)
{
write(x) ;
return *this ;
}
qostream& operator<<(const char* s)
{
write(s) ;
return *this ;
}
} qcout ;
}
using Read::qcin ;
using Write::qcout ;
}
using namespace IO ;
#define int long long
int n = 0 , q = 0 ;
set<int> color[100005] ;
int fa[100005] = {} , sum[100005] = {} ;
vector<int> edge[100005] ;
void build(int now , int father)
{
fa[now] = father ;
int siz = edge[now].size() ;
for(int i = 0 ; i < siz ; ++i)
if(edge[now][i] != father)
build(edge[now][i] , now) ;
}
void pushdown(int now , int c)
{
color[now].insert(c) ;
int siz = edge[now].size() ;
for(int i = 0 ; i < siz ; ++i)
if(edge[now][i] != fa[now])
pushdown(edge[now][i] , c) ;
}
void get_sum(int now)
{
sum[now] = color[now].size() ;
int siz = edge[now].size() ;
for(int i = 0 ; i < siz ; ++i)
if(edge[now][i] != fa[now])
{
get_sum(edge[now][i]) ;
sum[now] += sum[edge[now][i]] ;
}
}
signed main()
{
freopen("snow.in" , "r" , stdin) ;
freopen("snow.out" , "w" , stdout) ;
qcin>>n>>q ;
for(int i = 1 ; i < n ; ++i)
{
int a = 0 , b = 0 ;
qcin>>a>>b ;
edge[a].push_back(b) , edge[b].push_back(a) ;
}
build(1 , 1) ;
while(q--)
{
short op = 0 ;
qcin>>op ;
if(op == 1)
{
int x = 0 , c = 0 ;
qcin>>x>>c ;
pushdown(x , c) ;
get_sum(1) ;
}
else
{
int x = 0 ;
qcin>>x ;
qcout<<sum[x]<<'\n' ;
}
}
return 0 ;
}
正解分析
考虑我们上面的暴力算法其实不仅时间会炸,而且空间其实也是十分玄学的\(O(nc)\),不论怎样都可以被卡爆,所以我们考虑先从空间方面入手优化。
显然,\(set\)是必要的,因为我们不能让一个点被同一种颜色染两次,所以我们考虑反向存储来优化空间,即对于每一种颜色开一个\(set\),维护染成了这个颜色的点,这样空间复杂度便可以变成\(O(n)\)级别的。
然而,时间是还是无法过去的,所以考虑直接从答案贡献来源下手。
我们可以发现对于一个点(假定为\(u\))其实只存在两种情况:
- \(u\)祖先的子树均被染色,则\(u\)子树也会被染成该颜色,其贡献即为\(u\)子树的大小
- 若\(u\)子树已经被染成该颜色,则要加回来
显然,我们可以对这两种情况分别用一个树状数组进行维护,情况一为区间加,单点查;情况二为区间查,单点加。
正解代码
#include<iostream>
#include<cstring>
#include<vector>
#include<set>
using namespace std ;
namespace IO
{
const size_t SIZE = 1 << 16 ;
#define isdigit(ch) (ch >= '0' && ch <= '9')
namespace Read
{
char buf[SIZE] , *_now = buf , *_end = buf ;
char getchar()
{
if(_now == _end)
{
_now = _end = buf ;
_end += fread(buf , 1 , SIZE , stdin) ;
if(_now == _end)
return EOF ;
}
return *(_now++) ;
}
template<typename T>
void read(T& w)
{
w = 0 ;
short f = 1 ;
char ch = getchar() ;
while(!isdigit(ch))
{
if(ch == '-')
f = -1 ;
ch = getchar() ;
}
while(isdigit(ch))
w = (w << 1) + (w << 3) + (ch ^ 48) , ch = getchar() ;
w *= f ;
}
void read(char& ch)
{
char tmp = getchar() ;
while(tmp == ' ' || tmp == '\n')
tmp = getchar() ;
ch = tmp ;
}
void read(char* s)
{
char ch = getchar() ;
int len = 0 ;
while((ch == ' ' || ch == '\n') && ch != EOF)
ch = getchar() ;
while(ch != ' ' && ch != '\n' && ch != EOF)
s[len++] = ch , ch = getchar() ;
s[len] = '\0' ;
}
class qistream
{
public:
template<typename T>
qistream& operator>>(T& w)
{
read(w) ;
return *this ;
}
qistream& operator>>(char* s)
{
read(s) ;
return *this ;
}
} qcin ;
}
namespace Write
{
char buf[SIZE] , *p = buf ;
void flush()
{
fwrite(buf , 1 , p - buf , stdout) ;
p = buf ;
}
void putchar(char ch)
{
if(p == buf + SIZE)
flush() ;
*p = ch ;
++p ;
}
class Flush{public:~Flush(){flush() ;} ;}_;
template<typename T>
void write(T x)
{
char st[50] ;
int len = 0 ;
if(x < 0)
putchar('-') , x = -x ;
do
{
st[++len] = x % 10 + '0' ;
x /= 10 ;
} while(x) ;
while(len)
putchar(st[len--]) ;
}
void write(const char* s)
{
int len = strlen(s) ;
for(int i = 0 ; i < len ; ++i)
putchar(s[i]) ;
}
void write(char* s)
{
int len = strlen(s) ;
for(int i = 0 ; i < len ; ++i)
putchar(s[i]) ;
}
void write(char ch)
{
putchar(ch) ;
}
class qostream
{
public:
template<typename T>
qostream& operator<<(T x)
{
write(x) ;
return *this ;
}
qostream& operator<<(const char* s)
{
write(s) ;
return *this ;
}
} qcout ;
}
using Read::qcin ;
using Write::qcout ;
}
using namespace IO ;
int n = 0 , m = 0 ;
struct BIT
{
#define lowbit(x) (x & (-x))
int tree[100005] = {} ;
void add(int x , int y)
{
while(x <= n)
tree[x] += y , x += lowbit(x) ;
}
int query(int x)
{
int ans = 0 ;
while(x)
ans += tree[x] , x -= lowbit(x) ;
return ans ;
}
} t1 , t2 ;
vector<int> edge[100005] ;
int dfn[100005] = {} , siz[100005] = {} , id[100005] = {} , tot = 0 ;
set<int> color[100005] ;
void dfs(int now , int father)
{
dfn[now] = ++tot , id[tot] = now , siz[now] = 1 ;
int size = edge[now].size() ;
for(int i = 0 ; i < size ; ++i)
if(edge[now][i] != father)
dfs(edge[now][i] , now) , siz[now] += siz[edge[now][i]] ;
}
int main()
{
freopen("snow.in" , "r" , stdin) ;
freopen("snow.out" , "w" , stdout) ;
qcin>>n>>m ;
for(int i = 1 ; i < n ; ++i)
{
int a = 0 , b = 0 ;
qcin>>a>>b ;
edge[a].push_back(b) , edge[b].push_back(a) ;
}
dfs(1 , 1) ;
while(m--)
{
int op = 0 ;
qcin>>op ;
if(op == 1)
{
int x = 0 , c = 0 ;
qcin>>x>>c ;
auto it = color[c].lower_bound(dfn[x]) ;
if(it != color[c].begin() && dfn[id[*prev(it)]] + siz[id[*prev(it)]] >= dfn[x] + siz[x])
continue ;
while(it != color[c].end() && *it < dfn[x] + siz[x])
{
t1.add(*it , -1) ;
t1.add(*it + siz[id[*it]] , 1) ;
t2.add(*it , -siz[id[*it]]) ;
color[c].erase(it++) ;
}
color[c].insert(dfn[x]) , t1.add(dfn[x] , 1) , t1.add(dfn[x] + siz[x] , -1) , t2.add(dfn[x] , siz[x]) ;
}
else
{
int x = 0 ;
qcin>>x ;
qcout<<(siz[x] * t1.query(dfn[x]) + t2.query(dfn[x] + siz[x] - 1) - t2.query(dfn[x]))<<'\n' ;
}
}
return 0 ;
}

浙公网安备 33010602011771号