Loading

7312. 【2021.10.18NOIP提高组模拟】决斗

Description

\(n\le 10^5,m\le 2\times 10^5,0\le q\le n\)

Solution

将一场比赛看成一条边,胜者是败者的父亲,那么这题就是一个经典的异或最小生成树。

异或最小生成树版题:CF 888 G,https://codeforces.com/problemset/problem/888/G

不会的可以自行学习。

那么我们先求出异或最小生成树的答案,同时建出最小生成树。

然后就是一个更改边权的过程,这在最小生成树里是很典型的。

由于边权是 \(\&\) 上一个数,因此边权只会更小,找出两点之间路径的最大值,与更改后的边权比较,取更小的即可。

Code

#include<cstdio>
#include<algorithm>
#define N 100005
#define inf 1919810114514233333
#define ll long long 
using namespace std;
int n,m,tot=1,num,x,y,trie[N*60][2],idi[N*60],f[N][20],deep[N],size[60*N],rk[N];
ll ans,z,LCA,tep,g[N][20];
struct node
{
	int id; 
	ll val;
}a[N];
struct chge
{
	int to,next,head;
}tree[N<<1];
bool cmp(node x,node y) {return x.val<y.val;}
void ins(ll x,int id,int v)
{
	int u=1;
	for (int i=60;i>=0;--i)
	{
		int now=((x>>i)&1);
		if (!trie[u][now]) trie[u][now]=++tot;	
		u=trie[u][now];
		size[u]+=v;
	}
	idi[u]=id;
}
int get(ll x)
{
	int u=1;
	for (int i=60;i>=0;--i)
	{
		int now=((x>>i)&1);
		if (size[trie[u][now]]) u=trie[u][now];
		else u=trie[u][now^1];
	}
	return idi[u];
}
void add(int x,int y)
{
	tree[++num].to=y;
	tree[num].next=tree[x].head;
	tree[x].head=num;
}
void solve(int l,int r,int now)
{
	if (l>r) return;
	if (now<0)
	{
		for (int i=l;i<r;++i)
			add(a[i].id,a[i+1].id),add(a[i+1].id,a[i].id);
		return;
	}
	int mid=l-1;
	while (mid<r&&((a[mid+1].val>>now)&1)==0) ++mid;
	solve(l,mid,now-1);solve(mid+1,r,now-1);
	if (mid<l||mid>=r) return;
	for (int i=l;i<=mid;++i)
		ins(a[i].val,a[i].id,1);
	ll res=inf;
	int x=0,y=0;
	for (int i=mid+1;i<=r;++i)
	{
		int u=get(a[i].val);
		if ((a[rk[u]].val^a[i].val)<res) 
		{
			res=(a[rk[u]].val^a[i].val);
			x=u;
			y=a[i].id;
		}
	}
	ans+=res;
	add(x,y);add(y,x);
	for (int i=l;i<=mid;++i)
		ins(a[i].val,0,-1);
}
void dfs(int x,int fa)
{
	f[x][0]=fa;deep[x]=deep[fa]+1;
	for (int i=tree[x].head;i;i=tree[i].next)
	{
		int v=tree[i].to;
		if (v==fa) continue;
		g[v][0]=a[rk[x]].val^a[rk[v]].val;
		dfs(v,x);
	}
}
ll lca(int x,int y)
{
	ll res=0;
	if (deep[x]!=deep[y])
	{
		if (deep[x]<deep[y]) swap(x,y);
		for (int i=19;i>=0;--i)
			if (deep[f[x][i]]>=deep[y]) res=max(res,g[x][i]),x=f[x][i];
	}
	if (x==y) return res;
	for (int i=19;i>=0;--i)
		if (f[x][i]!=f[y][i]) res=max(res,max(g[x][i],g[y][i])),x=f[x][i],y=f[y][i];
	return max(res,max(g[x][0],g[y][0]));
}
int main()
{
	freopen("fight.in","r",stdin);
	freopen("fight.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;++i)
		scanf("%lld",&a[i].val),a[i].id=i;
	sort(a+1,a+n+1,cmp);
	for (int i=1;i<=n;++i)
		rk[a[i].id]=i;
	solve(1,n,60);
	dfs(1,0);
	printf("%lld\n",ans);
	for (int j=1;j<=19;++j)
		for (int i=1;i<=n;++i)
			f[i][j]=f[f[i][j-1]][j-1],g[i][j]=max(g[i][j-1],g[f[i][j-1]][j-1]);
	while (m--)
	{
		scanf("%d%d%lld",&x,&y,&z);
		LCA=lca(x,y);
		tep=((a[rk[x]].val^a[rk[y]].val)&z);
		printf("%lld\n",min(ans,ans-LCA+tep));
	}
	return 0;
}

posted @ 2021-10-20 11:45  Thunder_S  阅读(42)  评论(0编辑  收藏  举报