您的位置:首页 > 其它

hdu4897(树链剖分)

2016-07-25 18:39 197 查看
启迪:果然,离成功就差一步,如果刚刚我放弃了,停下debug的步伐,那我终不会知道成功原来并不遥远,就在霎那间,在我执着的信念前它终会出现

题目:树链剖分,细节真多,思路有一些乱,不过只要把所有情况考虑上了就ok了

注意事项在代码中

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
#define debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
inline int read()
{
int ans,f=1;char ch;
while ((ch=getchar())<'0'||ch>'9') if (ch=='-') f=-1;ans=ch-'0';
while ((ch=getchar())>='0'&&ch<='9') ans=ans*10+ch-'0';
return ans*f;
}
const int N=300090;
int to[N*2],pre[N*2],head[N*2],btot;//注意双向边数组开两倍
void addedge(int x,int y) {to[++btot]=y;pre[btot]=head[x];head[x]=btot;}// add edge
int dep
,fa
,top
,size
,son
,id
,tot;//tree devide
struct aa
{
int l,r,sum,rev;
int kk;//判断这个区间有没有被翻转
}a[N*4];//线段树开4倍
void init()//清空
{
memset(head,0,sizeof(head));btot=0;
memset(fa,0,sizeof(fa));
memset(top,0,sizeof(top));
memset(size,0,sizeof(size));
memset(son,0,sizeof(son));
memset(id,0,sizeof(id));tot=0;
memset(dep,0,sizeof(dep));
memset(a,0,sizeof(a));
}

int n,m;
void dfs1(int u,int depth,int f)
{
fa[u]=f;dep[u]=depth;size[u]=1;
int max_size=0;son[u]=0;
for (int i=head[u];i;i=pre[i])
if (to[i]!=f)
{
dfs1(to[i],depth+1,u);
size[u]+=size[to[i]];
if (size[to[i]]>max_size) max_size=size[to[i]],son[u]=to[i];
}
}
void dfs2(int u,int anc)
{
top[u]=anc;id[u]=++tot;
if (son[u]) dfs2(son[u],anc);
for (int i=head[u];i;i=pre[i])
if (to[i]!=fa[u]&&to[i]!=son[u]) dfs2(to[i],to[i]);
}//树链剖分
void build(int i,int l,int r)
{
a[i].l=l;a[i].r=r;
if (l==r) return;
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
}//建立线段树,把所有节点建立
void up(int i)
{
a[i].sum=0;
if (a[i<<1].l) a[i].sum+=a[i<<1].sum;
if (a[i<<1|1].l) a[i].sum+=a[i<<1|1].sum;//注意要保证有这个孩子再加,启下
}
void down(int i)
{
if (a[i].rev)
{
a[i].rev=0;a[i<<1].rev^=1;a[i<<1|1].rev^=1;
a[i<<1].sum=(a[i<<1].r-a[i<<1].l+1)-a[i<<1].sum;//因为,当i为子节点时,这个地方可能导致空节点num不为0,承上
a[i<<1|1].sum=(a[i<<1|1].r-a[i<<1|1].l+1)-a[i<<1|1].sum;
}
}
void rever(int i,int l,int r)//线段树中旋转区间
{
down(i);
if (a[i].l==l&&a[i].r==r)
{
a[i].rev^=1;
a[i].sum=(a[i].r-a[i].l+1)-a[i].sum;
return;
}
int mid=(a[i].l+a[i].r)>>1;
if (mid>=r) rever(i<<1,l,r);
else if (mid<l) rever(i<<1|1,l,r);
else rever(i<<1,l,mid),rever(i<<1|1,mid+1,r);
up(i);
}
void rever(int x,int y)//树上旋转x~y的路径,——询问1
{
while (top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y);
rever(1,id[top[x]],id[x]);
x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
if (x!=y) rever(1,id[x]+1,id[y]);//因为一个点代表它到father的这条边
}
void bj(int i,int l,int r)
{
if (a[i].l==l&&a[i].r==r)
{
a[i].kk^=1;
return;
}
int mid=(a[i].l+a[i].r)>>1;
if (mid>=r) bj(i<<1,l,r);
else if (mid<l) bj(i<<1|1,l,r);
else bj(i<<1,l,mid),bj(i<<1|1,mid+1,r);
}
int pan(int i,int x)
{
int ans=a[i].kk;
if (a[i].l==a[i].r) return ans;
int mid=(a[i].l+a[i].r)>>1;
if (mid>=x) return ans^pan(i<<1,x);else return ans^pan(i<<1|1,x);//一个点它是否被反转,就是从线段树根节点到这个子节点有多少包括他的区间被整体反转这样统计是否反转时,只需用从根查到子节点就好
}
int find(int i,int l,int r)
{
down(i);
if (a[i].l==l&&a[i].r==r) return a[i].sum;
int mid=(a[i].l+a[i].r)>>1;
if (mid>=r) return find(i<<1,l,r);
if (mid<l) return find(i<<1|1,l,r);
return find(i<<1,l,mid)+find(i<<1|1,mid+1,r);
}
void query(int x,int y)//x~y的黑边数量——询问3
{
int ans=0;
while (top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y);
if (x!=top[x]) ans+=find(1,id[top[x]]+1,id[x]);//处理重边的黑边number
ans+=(pan(1,id[fa[top[x]]])^find(1,id[top[x]],id[top[x]]));//pan判断父亲的标记,find边本身的标记,取异或,是否被变为黑色
x=fa[top[x]];//上面一定要是id【fa。。】我们把树信息移到线段树中了,操作都应该在线段树中修改
}
if (dep[x]>dep[y]) swap(x,y);
if (x!=y) ans+=find(1,id[x]+1,id[y]);
printf("%d\n",ans);
}
void near_rever(int x,int y)//路径邻边更改——询问2。。。。。实际上就是把一些边直接反转,一些边标记反转
{
while (top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y);
bj(1,id[top[x]],id[x]);//标记这些节点的所有轻儿子的边都被反转
if (son[x]) rever(1,id[son[x]],id[son[x]]);//如果有重儿子直接反转
rever(1,id[top[x]],id[top[x]]);//把最上面的轻边反转,和下一次循环时标记反转最底下的边抵消
x=fa[top[x]];
}
if (dep[x]>dep[y])swap(x,y);
bj(1,id[x],id[y]);
rever(1,id[x],id[x]);//把最上面的边反转
if (son[y]) rever(1,id[son[y]],id[son[y]]);
}
int main()
{
int T=read();
while (T--)
{
init();
n=read();
int x,y;
for (int i=1;i<n;i++)
{
x=read();y=read();
addedge(x,y);addedge(y,x);
}//add edge
dfs1(1,1,0);
dfs2(1,1);
build(1,1,tot);
int q=read(),t;
while (q--)
{
t=read(),x=read(),y=read();
switch(t)
{
case 1:rever(x,y);break;
case 2:near_rever(x,y);break;
case 3:query(x,y);break;//after switch,we must break!!!
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: