码迷,mamicode.com
首页 > 其他好文 > 详细

1760:树上数颜色

时间:2020-01-19 00:27:55      阅读:104      评论:0      收藏:0      [点我收藏+]

标签:modify   ==   差分   name   using   图片   lin   开始   树状数组   

1760:树上数颜色

时间限制: 3000 ms 内存限制: 524288 KB
提交数: 35 通过数: 9

【题目描述】
送你一棵n个点的树,树根为1。一开始每个点上有一个1∼n 的颜色ci,不同点颜色可以相同。

现在有 q 次操作, 分为两种类型:

1 u l r:询问子树 u 中有多少种在 l 到 r 之间的颜色至少出现了一次;

2 u c:将 u 的颜色修改为 c。

部分测试点要求强制在线。

【输入】
第一行三个整数n,q,t,分别表示树的点数,操作的个数和是否强制在线。

t=0表示不强制在线,t=1表示强制在线。

接下来一行nn个整数 ci,表示每个点的初始颜色。

接下来n−1行,每行两个整数ui;vi表示一条ui到vi的边。

接下来q行,每行四个或三个整数,表示一个操作。

当t=1时,需要对第一个数以外的其他数异或上一次询问的答案lastans,初始时lastans=0。

【输出】
对于每个询问输出一行一个整数,表示答案。

【输入样例】
5 5 0
5 5 2 5 5
5 1
2 5
4 2
3 5
1 2 2 3
2 5 1
1 1 1 5
2 3 2
1 3 1 5
【输出样例】
0
3
1
【提示】
【输入样例2】
5 5 1
4 1 1 5 4
5 1
3 5
2 3
4 3
2 5 4
2 2 2
1 3 1 5
2 1 2
1 1 2 7
【输出样例2】
3
1


【数据规模和约定】

对于前20%的数据,n,q≤5000n,q≤5000。

对于前40%的数据,n,q≤50000n,q≤50000。

对于另20%的数据,没有修改操作。

对于另20%的数据,t=0t=0。

对于100%的数据,1≤n,q≤1000001≤n,q≤100000。

 【题解】

考虑对于每种颜色,在子树中出现过就是他在dfn[u]到low[u]之间,但要求重复的颜色只算一次,可以将所有同一种颜色的节点按照dfn升序排列,树上差分维护,将所有点的权值加一,并将相邻的点的lca权值减一。对每个颜色建一颗线段树来维护区间求和。考虑使用树状数组套线段树优化,询问直接询问即可。实现较为复杂,代码如下:

技术图片
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
set <int> s[N];
int n,q,t,last[N],size,f[N][20],m,dfn[N],low[N],dep[N],cnt,dui[N],col[N],lastans;
struct pigu
{
    int dao,ne;
}a[N<<1];
inline void lingjiebiao(int x,int y)
{
    a[++size].dao=y;
    a[size].ne=last[x];
    last[x]=size;
}
inline int read()
{
    int x=0,f=1;
    char c=getchar();
    while(!isdigit(c)) {if(c==-) f=-1;c=getchar();} 
    while(isdigit(c)) {x=(x<<3)+(x<<1)+c-0;c=getchar();}
    return x*f;
}
int sum[N<<8],lc[N<<8],rc[N<<8],ge,root[N];
inline int get_lca(int x,int y)
{
    if(dep[x]<dep[y])
        swap(x,y);
    for(int i=19;i>=0;i--)
        if(dep[f[x][i]]>=dep[y]) 
            x=f[x][i];
    if(x==y) return x;
    for(int i=19;i>=0;i--)
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    return f[x][0];
}
inline void dfs(int now,int fa)
{
    dfn[now]=++cnt;dui[cnt]=now;
    f[now][0]=fa;dep[now]=dep[fa]+1;
    for(int i=1;f[f[now][i-1]][i-1];i++) f[now][i]=f[f[now][i-1]][i-1];
    for(int i=last[now];i;i=a[i].ne)
    {
        if(a[i].dao==fa) continue;
        dfs(a[i].dao,now);
    }
    low[now]=cnt;
}
inline int lowbit(int x)
{
    return x&(-x);
}
inline void modify(int &now,int l,int r,int zai,int sf)
{
    if(!now) now=++ge;
    sum[now]+=sf;
    if(l==r) return;
    int mid=(l+r)>>1;
    if(zai<=mid) modify(lc[now],l,mid,zai,sf);
    else modify(rc[now],mid+1,r,zai,sf);
}
inline void gainei(int se,int zai,int pan)
{
    set <int>::iterator it;
    it=s[se].find(zai);
    if(it!=s[se].begin())
    {
        it--;
        int ga=dui[*it];
        int gu=get_lca(dui[zai],dui[*it]);
        for(int i=se;i<=n;i+=lowbit(i)) modify(root[i],1,n,dfn[gu],-pan);
        it++;it++;
        if(it!=s[se].end())
        {    
            gu=get_lca(ga,dui[*it]);
            for(int i=se;i<=n;i+=lowbit(i)) modify(root[i],1,n,dfn[gu],pan);    
        }
        it--;
    }
    it++;
    if(it!=s[se].end())
    {
        int gu=get_lca(dui[zai],dui[*it]);
        for(int i=se;i<=n;i+=lowbit(i)) modify(root[i],1,n,dfn[gu],-pan);
        it--;
    }
}
inline void insert(int se,int zai)
{
    s[se].insert(dfn[zai]);gainei(se,dfn[zai],1);
    for(int i=se;i<=n;i+=lowbit(i))
        modify(root[i],1,n,dfn[zai],1);
}
inline int query(int now,int l,int r,int L,int R)
{
    if(!now) return 0;
    if(l>=L&&r<=R)
    {
        return sum[now];
    }
    int mid=(l+r)>>1,daan=0;
    if(L<=mid) daan+=query(lc[now],l,mid,L,R);
    if(R>=mid+1) daan+=query(rc[now],mid+1,r,L,R);
    return daan;
}
inline void shan(int se,int zai)
{
    gainei(se,dfn[zai],-1);s[se].erase(dfn[zai]);
    for(int i=se;i<=n;i+=lowbit(i)) modify(root[i],1,n,dfn[zai],-1);
} 
int main()
{
    n=read();q=read();t=read();
    for(int i=1;i<=n;i++) col[i]=read();
    for(int i=1,x,y;i<=n-1;i++)
    {
        x=read();y=read();
        lingjiebiao(x,y);
        lingjiebiao(y,x);
    }
    dfs(1,0);
    for(int i=1;i<=n;i++) insert(col[i],i);
    for(int i=1,x,y,z,u;i<=q;i++)
    {
        x=read();y=read();z=read();
        if(t==1) y^=lastans,z^=lastans;
        if(x==2)
        {
            shan(col[y],y);col[y]=z;
            insert(col[y],y);
        }
        else
        {
            u=read();
            if(t==1) u^=lastans;
            int ans=0;
            for(int i=u;i;i-=lowbit(i))
                ans+=query(root[i],1,n,dfn[y],low[y]);
            for(int i=z-1;i;i-=lowbit(i))
                ans-=query(root[i],1,n,dfn[y],low[y]);
            lastans=ans;
            cout<<ans<<"\n";
        }
    }
}
View Code

1760:树上数颜色

标签:modify   ==   差分   name   using   图片   lin   开始   树状数组   

原文地址:https://www.cnblogs.com/betablewaloot/p/12210739.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!