zoukankan      html  css  js  c++  java
  • 7312. 【2021.10.18NOIP提高组模拟】决斗

    Description

    (nle 10^5,mle 2 imes 10^5,0le qle 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
    ",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
    ",min(ans,ans-LCA+tep));
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    repadmin example.
    在 Windows 2000 和 Windows XP 中重置计算机帐户
    管理活动目录
    使用AdsiEdit工具查看GC数据
    mms链接media player 9.0无法打开
    活动目录的复制之细节
    使用Repadmin.exe 对活动目录复制排错
    Difference among Domain Local Group and Global Group and Universal Group
    使用 ADSI Edit 编辑 Active Directory 属性
    xp的密码工具
  • 原文地址:https://www.cnblogs.com/Livingston/p/15428251.html
Copyright © 2011-2022 走看看