#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;}
template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;}
const int N=4e5+10,M=0,Z=1e9+7,ms63=0x3f3f3f3f;
int n,m,x,y;
int col[N];
int first[N], id; int w[N << 1], nxt[N << 1]; bool vis[N];
int in[N], out[N]; int p[N]; int tim;
LL b[64];
LL C;
struct A
{
int l,r;
LL flag;
LL c;
}a[1<<20];
void ins(int x,int y)
{
++id;
w[id]=y;
nxt[id]=first[x];
first[x]=id;
}
void dfs(int x)
{
vis[x]=1;
in[x]=++tim;
p[tim]=col[x];
for(int z=first[x];z;z=nxt[z])
{
int y=w[z];
if(vis[y])continue;
dfs(y);
}
out[x]=tim;
}
void pushdown(int o)
{
if(a[o].flag)
{
a[ls].flag=a[rs].flag=a[o].flag;
a[ls].c |= a[o].flag;
a[rs].c |= a[o].flag;
a[o].flag=0;
}
}
void pushup(int o)
{
a[o].c=a[ls].c|a[rs].c;
}
void build(int o,int l,int r)
{
a[o].l = l;
a[o].r = r;
a[o].flag = 0;
if(l==r)
{
a[o].c|=b[ p[l] ];
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(o);
return;
}
void change(int o,int l,int r,LL v)
{
if(a[o].l==l&&a[o].r==r)
{
a[o].flag = a[o].c = v;
return;
}
pushdown(o);
int mid=(a[o].l+a[o].r)>>1;
if(r<=mid)change(ls,l,r,v);
else if(l>mid)change(rs,l,r,v);
else
{
change(ls,l,mid,v);
change(rs,mid+1,r,v);
}
pushup(o);
}
void update(int o,int l,int r)
{
if(a[o].flag)
{
C |= a[o].flag;
return;
}
if(a[o].l==l&&a[o].r==r)
{
C |= a[o].c;
return;
}
int mid=(a[o].l+a[o].r)>>1;
if(r<=mid)update(ls,l,r);
else if(l>mid)update(rs,l,r);
else
{
update(ls,l,mid);
update(rs,mid+1,r);
}
pushup(o);
}
int main()
{
for (int i = 0; i <= 60; ++i)b[i] = 1ll << i;
while(~scanf("%d%d",&n,&m))
{
memset(first,0,n+2<<2);id=1;
for(int i=1;i<=n;++i)scanf("%d",&col[i]);
for(int i=1;i<n;++i)
{
scanf("%d%d",&x,&y);
ins(x,y);
ins(y,x);
}
MS(vis,0);
tim=0;
dfs(1);
build(1,1,n);
for(int i=1;i<=m;++i)
{
int o;
int p,v;
scanf("%d",&o);
if(o==1)
{
scanf("%d%d",&p,&v);
change(1,in[p],out[p],b[v]);
}
else
{
scanf("%d",&p);
C = 0;
update(1,in[p],out[p]);
printf("%d\n", __builtin_popcountll(C));
}
}
}
return 0;
}
/*
【题意】
给你一棵树,有n(4e5)个节点,每个节点有一个颜色,颜色范围在[1,60]
我们有两种操作。
操作1:把节点p的子树都改为颜色v
操作2:查询以p为根的子树有多少种不同的颜色。
【类型】
线段树
dfs序
【分析】
我们先得到in和out的dfs序,
然后建线段树,每个节点开60个bool数组,表示颜色数
(或者用LL来表示)
然后对于修改操作,使用flag打标记
对于查询操作,做全局或运算,然后用__builtin_popcountll()求1的个数即可
【时间复杂度&&优化】
O(mlogn)
*/