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
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];
maphash_table;
queueqv;
vector >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[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]0)
    {
        if(last[x] 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

  

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

你可能感兴趣的:(BZOJ 4999:This Problem Is Too Simple!(树状数组差分维护DFS序+LCA+细节处理))