New Year Tree CodeForces - 620E(dfs序+线段树)

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<iostream>
#include<cstdio>
#include<vector>
#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;i<tree[root].size();i++)
    {
        int 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;i<tree[root].size();i++)
    {
        int 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<n;i++)
    {
        scanf("%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<<num<<endl;
        }
    }
    return 0;
}

最终优化版本(686ms)

#include<iostream>
#include<cstdio>
#include<vector>
#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<n;i++)
    {
        scanf("%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;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值