HDU的老爷机要跑14秒,其它机子只跑8秒。
Windows HDU,大常们梦结束的地方。
一道异或求和的题,根据套路,我们需要先拆位,然后就变成统计每一位0或1的数量。
这个求和的条件很奇怪。颜色相同,还好,可以开颜色种类棵动态开点线段树来维护01个数。令人纠结的地方就在于异或的两个节点升序,且互相不为祖先。
不少人此时就想到了容斥思路,先把所有颜色相同的异或和求出,再减去有祖先关系的异或和,最后除以2。这样的算法复杂度已经可接受,树剖跳链的复杂度是 O ( q log 2 n ⋅ 20 ) O(q\log^2n\cdot 20) O(qlog2n⋅20),把树剖优化掉的复杂度是 O ( q log n ⋅ 20 ) O(q\log n\cdot 20) O(qlogn⋅20)。 n ≤ 1 0 5 n\le 10^5 n≤105 ,这不稳过?
遗憾的是, T ≤ 8 T\le 8 T≤8,意味着你需要2秒内跑出一个点,而上面线段树的常数极大(可粗略乐观地估计为≥10),需要亿点精致的卡常。
怎么办,改用树状数组?树状数组需要离线+操作撤销,常数也立马大起来,一样不好卡常。
我们不妨绕开树剖,换一种思路。把” u < v u
这样一来,删除和更新点 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(qlogn⋅20) 的。再加上动态开点线段树的 O ( 1 ) O(1) O(1) 清空,顺利跑过HDU的老爷机。
#include //JZM yyds!!
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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;
}