[HDU6765] Count on a Tree II Striking Back
一、题目
点此看题(校内 \(\tt OJ\) 进不去别看我)
给定一棵 \(n\) 个点的树,每个点有颜色 \(c_i\),有 \(m\) 次操作:
- 修改某个点的颜色。
- 给出两条链 \(a\sim b\) 和 \(c\sim d\),询问这两条链上哪条颜色更多。
\(n\leq 10^5,m\leq 10^4\),多组数据,强制在线,保证两条链上的颜色数量至少差一倍。
二、解法
考虑充分利用题目性质,首先本题只要求判断大小关系,其次大小关系差距很大。这告诉我们可以用一些玄学算法来模糊地描述大小关系,可以多随机几次来提高正确率。
考虑对每种颜色均匀随机一个 \([0,2^{60})\) 之间的权值,定义链的权值为链上颜色的最小权值,那么拥有更多颜色的链期望拥有更小的权值,我们可以通过权值的大小关系来判断颜色数量的大小关系。
具体来说我们用 \(30\) 个不同版本的随机数,得到权值总和更小的就判定为拥有颜色数更多。
#include <cstdio>
#include <random>
#include <cstdlib>
#include <iostream>
#include <ctime>
using namespace std;
const int M = 500005;
const int N = 32;
#define ll long long
#define zz __int128
const ll inf = (1ll<<60)-1;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int T,n,m,k,s[M][N],a[M],b[N],t[M<<2][N],id[M];
int cnt,num[M],top[M],fa[M],dep[M],siz[M],son[M];
vector<int> g[M];
void dfs1(int u,int p)
{
siz[u]=1;fa[u]=p;
dep[u]=dep[p]+1;
for(int v:g[u]) if(v^p)
{
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
void dfs2(int u,int tp)
{
top[u]=tp;num[u]=++cnt;id[cnt]=u;
if(son[u]) dfs2(son[u],tp);
for(int v:g[u]) if(v^fa[u] && v^son[u])
dfs2(v,v);
}
void merge(int *a,int *b,int *c)
{
static int r[32]={};
for(int i=1;i<=k;i++) r[i]=min(b[i],c[i]);
for(int i=1;i<=k;i++) a[i]=r[i];
}
void build(int i,int l,int r)
{
if(l==r)
{
for(int j=1;j<=k;j++)
t[i][j]=s[a[id[l]]][j];
return ;
}
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
merge(t[i],t[i<<1],t[i<<1|1]);
}
void ins(int i,int l,int r,int p)
{
if(l==r)
{
for(int j=1;j<=k;j++)
t[i][j]=s[a[id[l]]][j];
return ;
}
int mid=(l+r)>>1;
if(mid>=p) ins(i<<1,l,mid,p);
else ins(i<<1|1,mid+1,r,p);
merge(t[i],t[i<<1],t[i<<1|1]);
}
void ask(int i,int l,int r,int L,int R)
{
if(L>r || l>R) return ;
if(L<=l && r<=R) {merge(b,b,t[i]);return ;}
int mid=(l+r)>>1;
ask(i<<1,l,mid,L,R);
ask(i<<1|1,mid+1,r,L,R);
}
zz qry(int u,int v)
{
zz res=0;
for(int i=1;i<=k;i++) b[i]=inf;
while(top[u]^top[v])
{
if(dep[top[u]]<dep[top[v]]) swap(u,v);
ask(1,1,n,num[top[u]],num[u]);
u=fa[top[u]];
}
if(dep[u]<dep[v]) swap(u,v);
ask(1,1,n,num[v],num[u]);
for(int i=1;i<=k;i++) res+=b[i];
return res;
}
void work()
{
n=read();m=read();cnt=0;
for(int i=1;i<=n;i++)
{
num[i]=top[i]=fa[i]=dep[i]=siz[i]=son[i]=0;
g[i].clear();
}
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);
}
dfs1(1,0);dfs2(1,1);build(1,1,n);
int ans=0;
while(m--)
{
int op=read(),x=read()^ans,y=read()^ans;
if(op==1)
{
a[x]=y;ins(1,1,n,num[x]);
continue;
}
zz r1=qry(x,y);x=read()^ans;y=read()^ans;
zz r2=qry(x,y);
if(r1<r2) ans++;
puts((r1<r2)?"Yes":"No");
}
}
signed main()
{
T=read();k=30;
//initialize some random value
mt19937 z(time(0));
for(int i=1;i<M;i++)
for(int j=1;j<=k;j++)
s[i][j]=z()%inf;
while(T--) work();
}