The New Year holidays are over, but Resha doesn’t want to throw away the New Year tree. He invited his best friends Kerim and Gural to help him to redecorate the New Year tree.
The New Year tree is an undirected tree with n vertices and root in the vertex 1.
You should process the queries of the two types:
Change the colours of all vertices in the subtree of the vertex v to the colour c.
Find the number of different colours in the subtree of the vertex v.
Input
The first line contains two integers n, m (1 ≤ n, m ≤ 4·105) — the number of vertices in the tree and the number of the queries.
The second line contains n integers ci (1 ≤ ci ≤ 60) — the colour of the i-th vertex.
Each of the next n - 1 lines contains two integers xj, yj (1 ≤ xj, yj ≤ n) — the vertices of the j-th edge. It is guaranteed that you are given correct undirected tree.
The last m lines contains the description of the queries. Each description starts with the integer tk (1 ≤ tk ≤ 2) — the type of the k-th query. For the queries of the first type then follows two integers vk, ck (1 ≤ vk ≤ n, 1 ≤ ck ≤ 60) — the number of the vertex whose subtree will be recoloured with the colour ck. For the queries of the second type then follows integer vk (1 ≤ vk ≤ n) — the number of the vertex for which subtree you should find the number of different colours.
Output
For each query of the second type print the integer a — the number of different colours in the subtree of the vertex given in the query.
Each of the numbers should be printed on a separate line in order of query appearing in the input.
Example
Input
7 10
1 1 1 1 1 1 1
1 2
1 3
1 4
3 5
3 6
3 7
1 3 2
2 1
1 4 3
2 1
1 2 5
2 1
1 6 4
2 1
2 2
2 3
Output
2
3
4
5
1
2
更新:这题的做法是通过dfs序把树上的节点映射到一个连续的区间,然后转化为线段树查询来做。
做这道题有点曲折.
第一发Wrong Anwser:我大概是知道是整数一默认为int型,左移超过32位了,所以需要后加LL标记来说明是long long类型常量,修改。
第二发Time limit exceeded on 35 test:我把所有的输入流都改成了标准输入流,修改。
第三发:Time limit exceeded on 36 test:我把原始vector存图优化为静态链表存图,修改。
第四发Time limit exceeded on 43 test:我优化树节点到连续数组index的预处理,只dfs了一次(之前的那个方法的确有点麻烦,需要两次)
第五发Time limit exceeded on 43 test:我没办法了,理论上是这么做没问题的。
后来,经提醒,我发现之前提交的选择的编译器是GNU C++17 Diagnostics (DrMemory),后来选用为GNU G++11 5.1.0,秒过。然后我把最原始的版本提交以后发现也过了,但是效率就不高了。但是不知道为什么编译器会造成这么大的效率差距?想不明白。因此浪费我好多时间。
评价:树的点被映射了两次,第一次是映射到了线性数组的index,然后index映射到线段树的st。
原始版本(2464ms)
#include
#include
#include
#define maxx 400005
using namespace std;
vector<int> tree[maxx];
int a[maxx];
int _size[maxx];
int n,m;
int index[maxx];//第一种是找到树在连续区间的坐标,然后如果找到这个点为子树的size,那么就知道了他的右边界,我是这样来找区间的。
int c[maxx];
void getSize(int root,int fa)
{
for(int i=0;iint v=tree[root][i];
if(v==fa)
continue;
getSize(v,root);
_size[root]+=_size[v];
}
_size[root]++;
}
void refl(int root,int fa,int l,int r)
{
index[root]=l;
c[l]=a[root];
if(l==r)
return;
int now=1;
for(int i=0;iint v=tree[root][i];
if(v==fa)
continue;
refl(v,root,l+now,l+now+_size[v]-1);
now+=_size[v];
}
}
long long q[maxx<<2];
int mark[maxx<<2];
void buildUp(int st,int l,int r)
{
if(l==r)
{
q[st]=1LL<<(c[l]-1);
return ;
}
int mid=(l+r)>>1;
buildUp(st<<1, l,mid);
buildUp(st<<1|1,mid+1,r);
q[st]=q[st<<1]|q[st<<1|1];
}
void pushDown(int st)
{
if(mark[st])
{
q[st<<1]=1LL<<(mark[st]-1);
mark[st<<1]=mark[st];
q[st<<1|1]=1LL<<(mark[st]-1);
mark[st<<1|1]=mark[st];
mark[st]=0;
}
}
void update(int st,int l,int r,int L,int R,int color)
{
if(L<=l&&r<=R)
{
q[st]=1LL<<(color-1);
mark[st]=color;
return;
}
pushDown(st);
int mid=(l+r)>>1;
if(L<=mid)
update(st<<1,l,mid,L,R,color);
if(R>mid)
update(st<<1|1,mid+1,r,L,R,color);
q[st]=q[st<<1]|q[st<<1|1];
}
long long query(int st,int l,int r,int L,int R)
{
if(L<=l&&r<=R)
return q[st];
pushDown(st);
long long ans=0;
int mid=(l+r)>>1;
if(L<=mid)
ans|=query(st<<1,l,mid,L,R);
if(R>mid)
ans|=query(st<<1|1,mid+1,r,L,R);
return ans;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
scanf("%d",a+i);
int x,y;
for(int i=1;i"%d%d",&x,&y);
tree[x].push_back(y);
tree[y].push_back(x);
}
int z;
getSize(1,-1);
refl(1,-1,1,n);
buildUp(1,1,n);
while(m--)
{
cin>>z;
if(z==1)
{
scanf("%d%d",&x,&y);
int L=index[x];
int R=L+_size[x]-1;
update(1,1,n,L,R,y);
}
else
{
cin>>x;
int L=index[x];
int R=L+_size[x]-1;
long long ans=query(1,1,n,L,R);
int num=0;
for(int i=0;i<60;i++)
if((ans>>i)&1)
num++;
cout<return 0;
}
最终优化版本(686ms)
#include
#include
#include
#define maxx 400005
using namespace std;
int head[maxx];
int to[maxx<<1];
int nextt[maxx<<1];
int cnt=1;
int a[maxx];
int n,m;
int c[maxx];
int _L[maxx],_R[maxx];
void addEdge(int x,int y)
{
to[cnt]=y;
nextt[cnt]=head[x];
head[x]=cnt++;
to[cnt]=x;
nextt[cnt]=head[y];
head[y]=cnt++;
}
int dfn=0;
void dfs(int root,int fa)//优化后直接用时间序来标志即可,代码简单易实现。
{
_L[root]=++dfn;
c[dfn]=a[root];
for(int i=head[root];i;i=nextt[i])
{
int v=to[i];
if(v==fa)
continue;
dfs(v,root);
}
_R[root]=dfn;
}
long long q[maxx<<2];
int mark[maxx<<2];
void buildUp(int st,int l,int r)
{
if(l==r)
{
q[st]=1LL<<(c[l]-1);
return ;
}
int mid=(l+r)>>1;
buildUp(st<<1, l,mid);
buildUp(st<<1|1,mid+1,r);
q[st]=q[st<<1]|q[st<<1|1];
}
void pushDown(int st)
{
if(mark[st])
{
q[st<<1]=1LL<<(mark[st]-1);
mark[st<<1]=mark[st];
q[st<<1|1]=1LL<<(mark[st]-1);
mark[st<<1|1]=mark[st];
mark[st]=0;
}
}
void update(int st,int l,int r,int L,int R,int color)
{
if(L<=l&&r<=R)
{
q[st]=1LL<<(color-1);
mark[st]=color;
return;
}
pushDown(st);
int mid=(l+r)>>1;
if(L<=mid)
update(st<<1,l,mid,L,R,color);
if(R>mid)
update(st<<1|1,mid+1,r,L,R,color);
q[st]=q[st<<1]|q[st<<1|1];
}
long long query(int st,int l,int r,int L,int R)
{
if(L<=l&&r<=R)
return q[st];
pushDown(st);
long long ans=0;
int mid=(l+r)>>1;
if(L<=mid)
ans|=query(st<<1,l,mid,L,R);
if(R>mid)
ans|=query(st<<1|1,mid+1,r,L,R);
return ans;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
scanf("%d",a+i);
int x,y;
for(int i=1;i"%d%d",&x,&y);
addEdge(x,y);
}
int z;
dfs(1,-1);
buildUp(1,1,n);
while(m--)
{
scanf("%d",&z);
if(z==1)
{
scanf("%d%d",&x,&y);
update(1,1,n,_L[x],_R[x],y);
}
else
{
scanf("%d",&x);
long long ans=query(1,1,n,_L[x],_R[x]);
int num=0;
for(int i=0;i<60;i++)
if((ans>>i)&1)
num++;
printf("%d\n",num);
}
}
return 0;
}