BZOJ 4999:This Problem Is Too Simple!(树状数组差分维护DFS序+LCA+细节处理)

4999: This Problem Is Too Simple!

Time Limit: 10 Sec  Memory Limit: 256 MB

Description

给您一颗树,每个节点有个初始值。
现在支持以下两种操作:
1. C i x(0<=x<2^31) 表示将i节点的值改为x。
2. Q i j x(0<=x<2^31) 表示询问i节点到j节点的路径上有多少个值为x的节点。

Input

第一行有两个整数N,Q(1 ≤N≤ 100,000;1 ≤Q≤ 200,000),分别表示节点个数和操作个数。
下面一行N个整数,表示初始时每个节点的初始值。
接下来N-1行,每行两个整数x,y,表示x节点与y节点之间有边直接相连(描述一颗树)。
接下来Q行,每行表示一个操作,操作的描述已经在题目描述中给出。

Output

对于每个Q输出单独一行表示所求的答案。

Sample Input

5 6
10 20 30 40 50
1 2
1 3
3 4
3 5
Q 2 3 40
C 1 40
Q 2 3 40
Q 4 5 30
C 3 10
Q 4 5 30

Sample Output

0
1
1
0
 
题解
分别考虑每种权值a。
sum[x]表示x节点到根的路径上有多少个权值为a的节点。
将节点x权值修改为a等价于给以x节点为根的子树里每个节点sum加1,同理将a改为其它权值为减1。
于是有Query(x,y)=Query(x)+Query(y)- Query(lca(x,y))-Query(fa[lca(x,y)])。
然后稍有常识的人都知道,一个节点的子树的DFS序是连续的。
所以将以x节点为根的子树里每个节点sum加1可以通过DFS序的性质转化成对一段连续序列加1。
根据上面的式子可以知道:对于求Query(x,y),每次是询问单点的值。
那么。。。
区间修改,单点求值!
显然的树状数组差分维护。。。
若是对于每个权值a都要开个树状数组,不就爆空间了么?
下面考虑优化空间。
这题看似是在线的,不过仔细分析可以发现对于每个权值的操作和询问都是独立的,只与此权值有关,和其他的互不影响。那么我们可以强制离线!
也就是说先存下信息,最后对于每个权值按照原先顺序进行此权值所有更改操作和求一遍涉及到此权值的所有答案。
这样树状数组可以重复利用。也就不用担心爆内存的问题了。。。
另外,细节还是要注意的,开始我就是没注意细节,WA声一片,数组也要开大些(BZOJ鬼畜的报了RE......)。
然后,详细的就看我的代码吧。
 
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
 
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
 
int buf[30];
inline void write(int x)
{
    if(x<0)putchar('-'),x=-x;
    buf[0]=0;
    while(x)buf[++buf[0]]=x%10,x/=10;
    if(!buf[0])buf[0]=1,buf[1]=0;
    while(buf[0])putchar('0'+buf[buf[0]--]);
    putchar('\n');
}
 
int n,q,x,y,z,cnt,cntx,ord;
int v[200005],last[200005];
int st[200005],en[200005];
char opt;
struct sdt
{
    int to,nxt;
}a[200005];
int head[200005];
int deep[200005],par[200005],son[200005],top[200005],siz[200005],delta[200005];
int c[200005],ans[200005];
map<int,int>hash_table;
queue<int>qv;
vector<pair<int,int> >cmd[200005];
 
inline void add(int x,int y)
{
    a[++cnt].to=y;
    a[cnt].nxt=head[x];
    head[x]=cnt;
}
 
void build(int x)
{
    siz[x]=1;
    deep[x]=deep[par[x]]+1;
    for(int i=head[x];i;i=a[i].nxt)
    {
        int v=a[i].to;
        if(par[v] || par[x]==v)continue;
        par[v]=x;
        build(v);
        siz[x]+=siz[v];
        if(siz[v]>siz[son[x]])son[x]=v;
    }
}
 
void find(int x)
{
    if(son[x])top[son[x]]=top[x]; else return ;
    for(int i=head[x];i;i=a[i].nxt)
    {
        int v=a[i].to;
        if(par[x]==v)continue;
        if(son[x]!=v)top[v]=v;
        find(v);
    }
}
 
int query(int x,int y)
{
    if(top[x]==top[y])if(deep[x]<deep[y])return x; else return y;
    else if(deep[top[x]]>deep[top[y]])x=par[top[x]]; else y=par[top[y]];
    query(x,y);
}
 
void dfn(int p,int fa)
{
    st[p]=++cntx;
    for(int i=head[p];i;i=a[i].nxt)
    {
        int v=a[i].to;
        if(v==fa)continue;
        dfn(v,p);
    }
    en[p]=cntx;
}
 
inline void modify(int x,int k,int col)
{
    while(x<=n)
    {
        if(last[x]<col)last[x]=col,c[x]=0;
        c[x]+=k;
        x+=lowbit(x);
    }
}
 
inline int get(int x,int col)
{
    int s=0;
    while(x>0)
    {
        if(last[x]<col)last[x]=col,c[x]=0;
        s+=c[x];
        x-=lowbit(x);
    }
    return s;
}
 
inline void solve(int col)
{
    int t=-1,tot=0;
    for(int i=0;i<cmd[col].size();i++)
    {
        pair<int,int> w=cmd[col][i];
        if(w.second<=1)
        {
            modify(st[w.first],w.second,col);
            modify(en[w.first]+1,-w.second,col);
        }
        else
        {
            w.first=st[w.first];
            if(w.second!=t)
            {
                ans[w.second-1]+=get(w.first,col); 
                tot=1;
            }
            else
            {
                tot++;
                if(tot==2)ans[t-1]+=get(w.first,col); else ans[t-1]-=get(w.first,col);
            }
        }
        t=w.second;
    }
}
 
int main()
{
    n=read();q=read();
    for(int i=1;i<=n;i++)
    {
        v[i]=read();
        if(hash_table.find(v[i])!=hash_table.end())
        {
            v[i]=hash_table[v[i]];
            cmd[v[i]].push_back(make_pair(i,1));
            continue;
        }
        hash_table[v[i]]=++ord;
        v[i]=ord;
        cmd[ord].push_back(make_pair(i,1));
    }
    for(int i=1;i<n;i++)
    {
        x=read();y=read();
        add(x,y);
        add(y,x);
    }
    deep[0]=-1;
    build(1);
    top[1]=1;
    find(1);
    dfn(1,0);
    for(int i=1;i<=q;i++)
    {
        while(getchar()!='\n'); 
        opt=getchar();
        if(opt=='C')
        {
            x=read();y=read();
            cmd[v[x]].push_back(make_pair(x,-1));
            if(hash_table.find(y)!=hash_table.end())v[x]=hash_table[y]; else hash_table[y]=++ord,v[x]=ord;
            cmd[v[x]].push_back(make_pair(x,1));
        }
        else
        {
            qv.push(i);
            x=read();y=read();z=read();
            if(hash_table.find(z)==hash_table.end()){ans[i]=0;continue;}
            z=hash_table[z];
            cmd[z].push_back(make_pair(x,i+1));
            cmd[z].push_back(make_pair(y,i+1));
            int lca=query(x,y);
            cmd[z].push_back(make_pair(lca,i+1));
            if(par[lca])cmd[z].push_back(make_pair(par[lca],i+1));
        }
    }
    for(int i=1;i<=ord;i++)solve(i);
    while(!qv.empty())write(ans[qv.front()]),qv.pop();
    return 0;
}

  

转载于:https://www.cnblogs.com/winmt/p/7451199.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值