bzoj 3637: Query on a tree VI 树链剖分 && AC600

3637: Query on a tree VI

Time Limit: 8 Sec  Memory Limit: 1024 MB
Submit: 206  Solved: 38
[Submit][Status][Discuss]

Description

You are given a tree (an acyclic undirected connected graph) with n nodes. The tree nodes are numbered from 1 to n.

Each node has a color, white or black. All the nodes are black initially.

We will ask you to perfrom some instructions of the following form:

  • 0 u : ask for how many nodes are connected to u, two nodes are connected iff all the node on the path from u to v (inclusive u and v) have a same color.
  • 1 u : toggle the color of u(that is, from black to white, or from white to black).

Input

The first line contains a number n denoted how many nodes in the tree(1 ≤ n ≤ 105). The next n - 1 lines, each line has two numbers (u,  v) describe a edge of the tree(1 ≤ u,  v ≤ n). The next line contains a number m denoted how many operations we are going to process(1 ≤ m ≤ 105). The next m lines, each line describe a operation (t,  u) as we mentioned above(0 ≤ t ≤ 1, 1 ≤ u ≤ n).

Output

For each query operation, output the corresponding result.

Sample Input

5
1 2
1 3
1 4
1 5
3
0 1
1 1
0 1

Sample Output

5
1

HINT

Source

  这道题常数卡的有点紧,我树链剖分用一棵线段树存就TLE了,每个链分别建线段树才行。

  考虑将每一个同色块的答案保存在这一块深度最浅的那一个点(这是一个很好的思路),我们考虑如何维护即可,对于每一个点,我们维护f[now][0/1]表示当前点如果取白色/黑色,所在的子树中与这个点同色的联通块大小。

  每次颜色修改只会影响到当前点到根节点路径上的一段。而且还是路径加减一个数,这可以用链剖维护。

  询问时只用跳到当前联通块最上方的点,然后输出该点所存的f值即可。

  

  AC600了,lalala~~

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 101000
#define MAXV MAXN*2
#define MAXE MAXV*2
#define MAXT MAXN*4
#define lch sgt[now].lc
#define rch sgt[now].rc
#define smid ((l+r)>>1)
#define INF 0x3f3f3f3f
struct Edge
{
        int np;
        Edge *next;
}E[MAXE],*V[MAXV];
int tope=-1;
void addedge(int x,int y)
{
        E[++tope].np=y;
        E[tope].next=V[x];
        V[x]=&E[tope];
}

int q[MAXN];
int pnt[MAXN];
int siz[MAXN];
int son[MAXN],top[MAXN],pos[MAXN],apos[MAXN],dfstime;
void bfs(int now)
{
        int head=-1,tail=0;
        Edge *ne;
        q[0]=now;
        pnt[now]=0;
        while (head<tail)
        {
                now=q[++head];
                for (ne=V[now];ne;ne=ne->next)
                {
                        if (ne->np==pnt[now])continue;
                        pnt[ne->np]=now;
                        q[++tail]=ne->np;
                }
        }
        for (int i=tail;i>=0;i--)
        {
                now=q[i];
                siz[now]=1;
                int mxsiz=0;
                for (ne=V[now];ne;ne=ne->next)
                {
                        if (ne->np==pnt[now])continue;
                        siz[now]+=siz[ne->np];
                        if (siz[ne->np]>mxsiz)
                        {
                                mxsiz=now;
                                son[now]=ne->np;
                        }
                }
        }
}
int stack[MAXN],tops=-1;
void dfs(int now)
{
        Edge *ne;
        stack[++tops]=now;
        top[now]=now;
        while (~tops)
        {
                now=stack[tops--];
                pos[now]=++dfstime;
                apos[dfstime]=now;
                for (ne=V[now];ne;ne=ne->next)
                {
                        if (ne->np==pnt[now] || ne->np==son[now])continue;
                        stack[++tops]=ne->np;
                        top[ne->np]=ne->np;
                }
                if (son[now])
                {
                        stack[++tops]=son[now];
                        top[son[now]]=top[now];
                }
        }
}
int col[MAXN];
int ptra[MAXN];
struct sgt_node
{
        int lc,rc;
        int sum[2];
        int pls[2];
        int val[2];
}sgt[MAXT];
int topt=0;
void make_plus(int now,int c,int d)
{
        sgt[now].val[c]+=d;
        sgt[now].pls[c]+=d;
}
void down(int now)
{
        if (sgt[now].pls[0])
        {
                make_plus(lch,0,sgt[now].pls[0]);
                make_plus(rch,0,sgt[now].pls[0]);
                sgt[now].pls[0]=0;
        }
        if (sgt[now].pls[1])
        {
                make_plus(lch,1,sgt[now].pls[1]);
                make_plus(rch,1,sgt[now].pls[1]);
                sgt[now].pls[1]=0;
        }
}
int Build_sgt(int l,int r)
{
        int now=++topt;
        sgt[now].val[0]=sgt[now].val[1]=sgt[now].pls[0]=sgt[now].pls[1]=0;
        sgt[now].sum[1]=1;
        sgt[now].sum[0]=0;
        if (l==r)
        {
                ptra[l]=now;
                sgt[now].val[1]=siz[apos[l]];
                sgt[now].val[0]=1;
                return now;
        }
        lch=Build_sgt(l,smid);
        rch=Build_sgt(smid+1,r);
        return now;
}
pair<int,int> Query_sgt(int now,int l,int r,int pos)
{
        if (l==r)
                return make_pair(sgt[now].val[0],sgt[now].val[1]);
        down(now);
        if (pos<=smid)
                return Query_sgt(lch,l,smid,pos);
        else
                return Query_sgt(rch,smid+1,r,pos);
}
void Modify_sgt(int now,int l,int r,int x,int y,int c,int d)
{
        if (l==x && r==y)
        {
                make_plus(now,c,d);
                return ;
        }
        down(now);
        if (y<=smid)
                return Modify_sgt(lch,l,smid,x,y,c,d);
        else if (smid<x)
                return Modify_sgt(rch,smid+1,r,x,y,c,d);
        else
        {
                Modify_sgt(rch,smid+1,r,smid+1,y,c,d);
                Modify_sgt(lch,l,smid,x,smid,c,d);
        }
}
void Modify_sgt2(int now,int l,int r,int pos)
{
        if (l==r)
                return swap(sgt[now].sum[0],sgt[now].sum[1]);
        down(now);
        if (pos<=smid)
                Modify_sgt2(lch,l,smid,pos);
        else
                Modify_sgt2(rch,smid+1,r,pos);
        sgt[now].sum[0]=sgt[lch].sum[0]+sgt[rch].sum[0];
        sgt[now].sum[1]=sgt[lch].sum[1]+sgt[rch].sum[1];
}
int Scan_sgt(int now,int l,int r,int x,int y,int c)
{
        if (l==x && r==y)
        {
                if (sgt[now].sum[c]==(r-l+1))
                {
                        return l;
                }else if (sgt[now].sum[c]==0)
                {
                        return -1;
                }else
                {
                        down(now);
                        int ret=Scan_sgt(rch,smid+1,r,smid+1,y,c);
                        if (ret==smid+1)
                        {
                                ret=Scan_sgt(lch,l,smid,x,smid,c);
                                if (ret==-1)return smid+1;
                                else return ret;
                        }else return ret;
                }
        }
        down(now);
        if (y<=smid)
                return Scan_sgt(lch,l,smid,x,y,c);
        else if (smid<x)
                return Scan_sgt(rch,smid+1,r,x,y,c);
        else
        {
                int ret=Scan_sgt(rch,smid+1,r,smid+1,y,c);
                if (ret==smid+1)
                {
                        ret=Scan_sgt(lch,l,smid,x,smid,c);
                        if (ret==-1)return smid+1;
                        else return ret;
                }else return ret;
        }
}
int spos[MAXN],tpos[MAXN];
int troot[MAXN];
int Swim_up(int x)
{
        int rpos=pos[x];
        int c=col[x];
        while (x)
        {
                int y=Scan_sgt(troot[top[x]],spos[top[x]],tpos[top[x]],pos[top[x]],pos[x],c);
                if (y==-1)break;
                else if (y!=pos[top[x]])
                {
                        rpos=y;break;
                }else
                {
                        rpos=y;
                        x=pnt[top[x]];
                }
        }
        return apos[rpos];
}
int main()
{
        freopen("input.txt","r",stdin);
        freopen("output.txt","w",stdout);
        int n,m;
        int x,y,z;
        scanf("%d",&n);
        for (int i=1;i<n;i++)
        {
                scanf("%d%d",&x,&y);
                addedge(x,y);
                addedge(y,x);
        }
        bfs(1);
        dfs(1);
        for (int i=1;i<=n;i++)
                spos[i]=INF,tpos[i]=-INF;
        for (int i=1;i<=n;i++)
                spos[top[i]]=min(spos[top[i]],pos[i]);
        for (int i=1;i<=n;i++)
                tpos[top[i]]=max(tpos[top[i]],pos[i]);
        for (int i=1;i<=n;i++)
                if (top[i]==i)
                        troot[i]=Build_sgt(spos[i],tpos[i]);
        scanf("%d",&m);
        int opt;
        for (int i=1;i<=n;i++)col[i]=1;
        for (int i=0;i<m;i++)
        {
                scanf("%d%d",&opt,&x);
                if (opt==0)
                {
                        int rpt=Swim_up(x);
                        pair<int,int> res=Query_sgt(troot[top[rpt]],spos[top[rpt]],tpos[top[rpt]],pos[rpt]);
                        if (col[x]==0)
                                printf("%d\n",res.first);
                        else
                                printf("%d\n",res.second);
                }else
                {
                        pair<int,int> res=Query_sgt(troot[top[x]],spos[top[x]],tpos[top[x]],pos[x]);
                        int p=pnt[x];
                        int c=col[p];
                        int d,d2;
                        if (col[p]==1 && col[x]==1)d=-res.second,d2=res.first;
                        else if (col[p]==1 && col[x]==0)d=res.second,d2=-res.first;
                        else if (col[p]==0 && col[x]==1)d=res.first,d2=-res.second;
                        else d=-res.first,d2=res.second;
                        if (p)
                        {
                                sgt[ptra[pos[p]]].val[c^1]+=d2;
                                int a=Swim_up(p);
                                a=pnt[a];
                                if (!a)a=1;
                                while (true)
                                {
                                        if (top[p]==top[a])
                                        {
                                                Modify_sgt(troot[top[a]],spos[top[a]],tpos[top[a]],pos[a],pos[p],c,d);
                                                break;
                                        }
                                        Modify_sgt(troot[top[p]],spos[top[p]],tpos[top[p]],pos[top[p]],pos[p],c,d);
                                        p=pnt[top[p]];
                                }
                        }
                        Modify_sgt2(troot[top[x]],spos[top[x]],tpos[top[x]],pos[x]);
                        col[x]^=1;
                }
        }
}
时间: 2024-07-31 08:28:18

bzoj 3637: Query on a tree VI 树链剖分 && AC600的相关文章

[BZOJ 3637]Query on a tree VI

偶然看见了这题,觉得自己 QTREE.COT 什么的都没有刷过的真是弱爆了…… 一道思路很巧妙的题,终于是在约大爷的耐心教导下会了,真是太感谢约大爷了. 这题显然是树链剖分,但是链上维护的东西很恶心.其核心思想是找到一个相连的最浅同色节点,那么我只要维护每个点的子树中与他相连的点的数量即可 用 f[c][u] 表示在 u 的子树中与 u 相连 (假设 u 无色) 且颜色为 c 的点数 查询直接算出与 u 相连的最浅同色节点 a,ans=f[c[u]][a] 考虑修改,我们发现每次 u 被反转,影

SPOJ 375 QTREE系列-Query on a tree (树链剖分)

题目地址:SPOJ 375 树链剖分第一发! 果然是个貌似很高级的数据结构,其实就是把树的边从树形结构转化成了线性结构,从而可以用线段树或树状数组之类的数据结构进行快速维护.从而将时间缩到n*log(2*n). 这题用的线段树维护的. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #incl

SPOJ 913 Query on a tree II ( 树链剖分 + 倍增 )

题目链接~~> 做题感悟:感觉又充实了一些. 解题思路:树链剖分 + 倍增 开始看时,第一问还好,第二问就不知道怎么解了.其实这两问都可以用倍增法解决. 先解释一下我理解的倍增 :记录 u 结点的 第 2 ^ i 个祖先,然后求u 的第 k 个祖先的时候,就相当于用 2 ^ i 去组合 k ,不断向上,一直到达第 k 个节点,其实每次更新的时k 的二进制中为 1 的位置.如下图,计算 u 的第 5 个祖先结点(这里不包括 u),先到达 u' 节点,然后再从 u' ,到 u'' (5 的二进制 1

【SPOJ Query on a tree 】 (树链剖分)

http://acm.hust.edu.cn/vjudge/problem/13013 题意: 有一棵N个节点的树(1<=N<=10000),N-1条边,边的编号为1~N-1,每条边有一个权值,要求模拟两种操作: 1:QUERY x y 求节点x和节点y之间的路径中权值最大的边. 2:CHANGE p k修改第p条边的权值为k. [分析] 树链剖分裸题.. [表示我一开始怎么TLE,后来怎么AC的并不知道.. 1 #include<cstdio> 2 #include<cst

SPOJ 375 QTREE - Query on a tree(树链剖分)

题目链接:http://www.spoj.com/problems/QTREE/en/ 题意:  一棵树 n 个节点,每条边上有权值,同时有两个操作: (1)更改操作:CHANGE i ti(把第 i 条边上的权值改为 ti). (2)查询操作:QUERY a b(查询 a 到 b 的路径上权值最大的边的权值). 思路(树链剖分): 看到这种区间查询的题想要用数据结构优化,提高时间效率一般会想到线段树.可是这次操作的对象并不是一组序列, 无法直接使用线段树.这时,我们可以做些转化:对树作树链剖分

SPOJ QTREE Query on a Tree【树链剖分模板题】

树链剖分,线段树维护~ #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <vector> using namespace std; const int MAXN = 10014; struct Edge { int to,next; }edge[MAXN*2]; int head[MAXN],tot; int top[MA

SPOJ 375 Query on a tree【树链剖分】

题目大意:给你一棵树,有两个操作1.修改一条边的值,2.询问从x到y路径上边的最大值 思路:如果树退化成一条链的话线段树就很明显了,然后这题就是套了个树连剖分,调了很久终于调出来第一个模板了 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define maxn 100009 5 using namespace std; 6 int head[maxn],next[maxn*2],point[

spoj 375. Query on a tree 【树链剖分--插点问线 】

题目:spoj 375. Query on a tree 题意:题意很清晰,就是给你一颗树,每两点之间有权值,然后改变一些权值,问一条路径上的最大值. 分析:入门题目,直接套树链模板 AC代码: #include <cstdio> #include <algorithm> #include <iostream> #include <string.h> using namespace std; const int N = 10010; #define Del(

SPOJ 375. Query on a tree (树链剖分)

Query on a tree Time Limit: 5000ms Memory Limit: 262144KB This problem will be judged on SPOJ. Original ID: QTREE64-bit integer IO format: %lld      Java class name: Main Prev Submit Status Statistics Discuss Next Font Size: + - Type:   None Graph Th