hdu4897(树链剖分)

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


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

注意事项在代码中


#include
#include
#include
#include
#include
#include
#define debug(x) cout<<#x<<"="<'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[N],fa[N],top[N],size[N],son[N],id[N],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 (middep[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>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 (middep[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[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




你可能感兴趣的:(树链剖分,线段树)