HDU6793 - Tokitsukaze and Colorful Tree——DFS序、线段树

Tokitsukaze and Colorful Tree

前言

HDU的老爷机要跑14秒,其它机子只跑8秒。

Windows HDU,大常们梦结束的地方。

题解

一道异或求和的题,根据套路,我们需要先拆位,然后就变成统计每一位0或1的数量。

这个求和的条件很奇怪。颜色相同,还好,可以开颜色种类棵动态开点线段树来维护01个数。令人纠结的地方就在于异或的两个节点升序,且互相不为祖先

不少人此时就想到了容斥思路,先把所有颜色相同的异或和求出,再减去有祖先关系的异或和,最后除以2。这样的算法复杂度已经可接受,树剖跳链的复杂度是 O ( q log ⁡ 2 n ⋅ 20 ) O(q\log^2n\cdot 20) O(qlog2n20),把树剖优化掉的复杂度是 O ( q log ⁡ n ⋅ 20 ) O(q\log n\cdot 20) O(qlogn20) n ≤ 1 0 5 n\le 10^5 n105 ,这不稳过?

遗憾的是, T ≤ 8 T\le 8 T8,意味着你需要2秒内跑出一个点,而上面线段树的常数极大(可粗略乐观地估计为≥10),需要亿点精致的卡常。

怎么办,改用树状数组?树状数组需要离线+操作撤销,常数也立马大起来,一样不好卡常。

我们不妨绕开树剖,换一种思路。把” u < v u<v u<v“转化成” d f n u < d f n v dfn_u<dfn_v dfnu<dfnv“,即两点的dfs序升序。进一步说,一个点只能与它dfs序后面的节点异或。然后考虑”互相不为祖先“的限制。由于一个点的祖先节点的dfs序必定小于它,所以后面的节点中只有在其子树内的节点不合法。我们知道在dfs序中,一个节点的子树范围是以自己为左端点的一个区间,不妨设 h d i hd_i hdi t l i tl_i tli 表示子树 i i i 区间的左右端点( h d i hd_i hdi 就等于 d f n i dfn_i dfni,所以后面直接用 h d i hd_i hdi 来代替后者),那么不用再考虑更多了,每个节点 i i i 的贡献就是与 ( t l i , n ] (tl_i,n] (tli,n] 中的同色节点异或的和!

这样一来,删除和更新点 i i i 的信息时只需要考虑所有 h d v ∈ ( t l i , n ] hd_v\in(tl_i,n] hdv(tli,n] 的节点 v v v,和所有 t l u ∈ [ 1 , h d i ) tl_u\in[1,hd_i) tlu[1,hdi) 的节点 u u u。分别维护两棵动态开点的线段树,做区间查询和单点修改即可(之前的做法是区间修改,常数是单点修改的四倍)。这个时候的查询常数并不是很优秀,于是再加一个小优化:维护区间内的点数,由于拆位后每一位上0和1的个数相加必为总点数,所以可以只维护1的个数,省掉一半时间和空间。

这时我们就得到了无懒标记下传、只有单点修改区间查询、常数小得多的普通线段树做法(常数可估计为<4),也是 O ( q log ⁡ n ⋅ 20 ) O(q\log n\cdot 20) O(qlogn20) 的。再加上动态开点线段树的 O ( 1 ) O(1) O(1) 清空,顺利跑过HDU的老爷机。

代码

#include<cstdio>//JZM yyds!!
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define ll long long
#define uns unsigned
#define MAXN 100003
#define INF 1e18
#define MOD 998244353ll
#define lowbit(x) ((x)&(-(x)))
#define IF it->first
#define IS it->second
#define rg register
using namespace std;
inline ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
	return f?x:-x;
}
int n,c[MAXN],a[MAXN];
int hd[MAXN],tl[MAXN],IN;
ll ans;
vector<int>G[MAXN];
inline void dfs(int x,int fa){
	hd[x]=++IN;
	for(uns i=0;i<G[x].size();i++)
		if(G[x][i]!=fa)dfs(G[x][i],x);
	tl[x]=IN;
}

struct nd{
	int a[20];
	nd operator+(const nd&b){
		nd r;
		for(rg int i=0;i<20;i++)r.a[i]=a[i]+b.a[i];
		return r;
	}
	void CL(){memset(a,0,sizeof(a));}
};
inline nd ZR(){nd r;r.CL();return r;}
struct itn{
	int ls,rs,s;
	void CL(){ls=rs=0,s=0,n1.CL();}
	nd n1;
}t[MAXN<<6];
int hr[MAXN],tr[MAXN];
inline void PU(int x){
	t[x].n1=t[t[x].ls].n1+t[t[x].rs].n1;
}
inline void add(int x,int l,int r,int z,int d,int iv){
	for(rg int i=0;i<20;i++)t[x].n1.a[i]+=((d>>i)&1)*iv;
	t[x].s+=iv;
	if(l==r)return;
	int mid=(l+r)>>1;
	if(z<=mid){
		if(!t[x].ls)t[x].ls=++IN,t[IN].CL();
		add(t[x].ls,l,mid,z,d,iv);
	}
	else{
		if(!t[x].rs)t[x].rs=++IN,t[IN].CL();
		add(t[x].rs,mid+1,r,z,d,iv);
	}
}
inline int getnum(int x,int l,int r,int a,int b){
	if(a>b)return 0;
	if(l==a&&r==b)return t[x].s;
	int mid=(l+r)>>1,res=0;
	if(a<=mid)res+=getnum(t[x].ls,l,mid,a,min(mid,b));
	if(b>mid)res+=getnum(t[x].rs,mid+1,r,max(a,mid+1),b);
	return res;
}
inline nd get1(int x,int l,int r,int a,int b){
	if(a>b)return ZR();
	if(l==a&&r==b)return t[x].n1;
	int mid=(l+r)>>1;nd res;res.CL();
	if(a<=mid)res=res+get1(t[x].ls,l,mid,a,min(mid,b));
	if(b>mid)res=res+get1(t[x].rs,mid+1,r,max(a,mid+1),b);
	return res;
}
inline void ins(int x,int y,int k){
	int nu=getnum(hr[y],1,n,tl[x]+1,n)+getnum(tr[y],1,n,1,hd[x]-1);
	nd m1=get1(hr[y],1,n,tl[x]+1,n)+get1(tr[y],1,n,1,hd[x]-1);
	for(int j=0;j<20;j++){
		if((k>>j)&1)ans+=(0ll+nu-m1.a[j])<<j;
		else ans+=(0ll+m1.a[j])<<j;
	}
	c[x]=y,a[x]=k;
	add(hr[y],1,n,hd[x],k,1);
	add(tr[y],1,n,tl[x],k,1);
}
inline void del(int x,int y,int k){
	int nu=getnum(hr[y],1,n,tl[x]+1,n)+getnum(tr[y],1,n,1,hd[x]-1);
	nd m1=get1(hr[y],1,n,tl[x]+1,n)+get1(tr[y],1,n,1,hd[x]-1);
	for(int j=0;j<20;j++){
		if((k>>j)&1)ans-=(0ll+nu-m1.a[j])<<j;
		else ans-=(0ll+m1.a[j])<<j;
	}
	add(hr[y],1,n,hd[x],k,-1);
	add(tr[y],1,n,tl[x],k,-1);
}
signed main()
{
	for(int T=read();T--;){
		n=read();
		
		IN=0,ans=0;
		for(int i=1;i<=n;i++)G[i].clear();
		
		for(int i=1;i<=n;i++)c[i]=read();
		for(int i=1;i<=n;i++)a[i]=read();
		for(int i=1;i<n;i++){
			int u=read(),v=read();
			G[u].push_back(v),G[v].push_back(u);
		}
		dfs(1,0),IN=0;
		for(int i=1;i<=n;i++)
			hr[i]=++IN,t[IN].CL(),tr[i]=++IN,t[IN].CL();
		for(int i=1;i<=n;i++){
			int cl=c[i];
			add(hr[cl],1,n,hd[i],a[i],1);
			add(tr[cl],1,n,tl[i],a[i],1);
		}
		for(int i=1;i<=n;i++){
			int cl=c[i],nu=getnum(hr[cl],1,n,tl[i]+1,n);
			nd m1=get1(hr[cl],1,n,tl[i]+1,n);
			for(int j=0;j<20;j++){
				if((a[i]>>j)&1)ans+=(0ll+nu-m1.a[j])<<j;
				else ans+=(0ll+m1.a[j])<<j;
			}
		}
		printf("%lld\n",ans);
		for(int M=read();M--;){
			int op=read(),x=read(),y=read();
			if(op==2)del(x,c[x],a[x]),ins(x,y,a[x]);
			else del(x,c[x],a[x]),ins(x,c[x],y);
			printf("%lld\n",ans);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值