Luogu P17091 三十四万一千七百九十九 题解

P17091 三十四万一千七百九十九

繁题,但确实能学到一些东西。

先考虑第 \(1\) 问。匹配过程分为两个阶段:每次选择两个数匹配以及剩下的数各自和 \(0\) 匹配。第二个阶段显然就是把剩下的数从小到大插入序列末尾。假设我们知道现在两两匹配得到的最小值为 \(g\),剩下的数最小值为 \(m\),开始大分讨。

如果 \(g\lt m\) 显然继续匹配更优,如果 \(g\gt m\) 显然所有数与 \(0\) 匹配更优。如果 \(g=m\gt 0\),那么两两匹配相当于用两个大于 \(m\) 的数造了一个 \(m\) 出来,且由于最小值大于 \(0\) 所以不会用掉 \(m\),因此继续匹配更优。如果 \(g=m=0\) 且还存在非 \(0\) 的数,那么让 \(0\) 两两匹配只会使非 \(0\) 的数到达序列更前面,所以所有数与 \(0\) 匹配更优。如果 \(g=m=0\) 且不存在非 \(0\) 的数,那么序列之后一定都是 \(0\),越短越好,因此继续匹配更优。

赛时止步于这个东西的 \(O(n^2)\) 实现,还没有调出来。其实这个东西的难点在于如何快速维护 \(g\),有一个结论:把所有数排序,这些数两两匹配的最小值一定在相邻的两个数取到。证明的话考虑 \(x\lt y\lt z\),找到 \(x,z\) 的最高不相等的二进制位,显然 \(y\) 比这一位更高的位必然与 \(x,z\) 相同,而这一位 \(y\) 要么取 \(1\) 要么取 \(0\)\(x\oplus y\)\(z\oplus y\) 中一定有一个这一位是 \(0\),更高位是 \(0\),而 \(x\oplus z\) 中这一位是 \(1\),更高位是 \(0\),因此相邻的一定有一个比不相邻的小,证毕。于是我们把 \(a\) 数组排序之后链表维护,用 set 或者可删堆维护相邻位置的异或值,按照上述大分讨模拟即可。

然后考虑第 \(2\) 问。先考虑 Sirus 的决策,如果所有数都相同,那么显然所有数放到同一个集合,否则 Dylan 可以搓一个 \(0\) 出来。否则,一定可以找到一个最高的位使得所有数在这一位上有差异。如果比这一位高的位上有值,也选择所有数放到同一个集合,否则 Dylan 可以在那一位上搓出一个 \(0\),由于那一位更高所以一定更劣;如果比这一位高的位上没有值,那么考虑尽量让这一位只能搓出 \(1\),于是考虑把这一位为 \(0\) 的丢第一个集合,这一位为 \(1\) 的丢第二个集合,这样 Dylan 就只能让这一位 \(0\)\(1\) 配对。如果 \(0\)\(1\) 多其实 Dylan 还是可以在这一位搓一个 \(0\) 出来,但如果把 \(0\) 丢到第二个集合或把 \(1\) 丢到第一个集合都会导致搓出来的 \(0\) 变多,不优,因此还是这么分。

然后考虑 Dylan 的决策,考虑 Trie 树上贪心,Trie 树上的节点记录子树内还没有用掉的数的个数。同时在两棵树上走路,每次优先选择都走 \(0\) 或都走 \(1\),然后再选择走一个 \(0\) 和一个 \(1\),如果其中一个子树内没数了就直接返回,走到叶子就根据两个叶子记录的数的个数的最小值决定匹配多少次。这一步复杂度是对的,因为每匹配一次只会用掉 \(O(\log V)\) 的时间。需要注意这一步得出来的序列不是有序的,因为可能出现都走 \(0\) 的子树中的一次匹配比都走 \(1\) 的子树中的一次匹配大,但由于深度优先会排在前面,因此还需要排序一下。

完成了这一步后,在两棵树中把还没有匹配的数捞出来,从小到大排序后丢到原序列的末尾。

时间复杂度 \(O(n\log n+n\log V)\),可以通过。

#include <bits/stdc++.h>
using namespace std;
int t,n,a[2000000],pr[2000000],nxt[2000000];
const int mod=1e9+7;
multiset<pair<int,pair<int,int> > >s;
vector<int>r,rst;
struct trie
{
	vector<int>ch[2],siz;
	int rt=0;
	void clear()
	{
		ch[0].clear(),ch[1].clear(),siz.clear(),rt=0;
	}
	int create()
	{
		ch[0].push_back(0),ch[1].push_back(0),siz.push_back(0);
		return (int)siz.size()-1;
	}
	void pushup(int x)
	{
		siz[x]=siz[ch[0][x]]+siz[ch[1][x]];
	}
	void insert(int v)
	{
		if(rt==0)create(),rt=create();
		int x=rt;
		siz[x]++;
		for(int i=30;i>=0;i--)
		    {
	    	int id=(v>>i)&1;
	    	if(!ch[id][x])
			   {
			   int y=create();
			   ch[id][x]=y;
		       }
	    	x=ch[id][x],siz[x]++;
			}
	}
	void find(int x,int dep,int now)
	{
		if(siz[x]==0)return;
		if(dep==-1)
		   {
		   	while(siz[x])rst.push_back(now),siz[x]--;
		   	return;
		   }
		find(ch[0][x],dep-1,now);
		find(ch[1][x],dep-1,now+(1<<dep));
		pushup(x);
	}
}tr[2];
void match()
{
	int x=(*s.begin()).first,l=-(*s.begin()).second.first,r=-(*s.begin()).second.second;
	s.erase({x,{-l,-r}});
	if(pr[l]!=0&&nxt[r]!=n+1)s.insert({a[pr[l]]^a[nxt[r]],{-pr[l],-nxt[r]}});
	if(pr[l]!=0)s.erase({a[pr[l]]^a[l],{-pr[l],-l}});
	if(nxt[r]!=n+1)s.erase({a[nxt[r]]^a[r],{-r,-nxt[r]}});
	pr[nxt[r]]=pr[l],nxt[pr[l]]=nxt[r];
}

void dfs(int x1,int x2,int dep,int now)
{
	if(tr[0].siz[x1]==0||tr[1].siz[x2]==0)return;
	if(dep==-1)
	   {
	   	int num=min(tr[0].siz[x1],tr[1].siz[x2]);
	   	for(int i=1;i<=num;i++)r.push_back(now);
	   	tr[0].siz[x1]-=num,tr[1].siz[x2]-=num;
	   	return;
	   }
	if(tr[0].ch[0][x1]&&tr[1].ch[0][x2])dfs(tr[0].ch[0][x1],tr[1].ch[0][x2],dep-1,now);
	if(tr[0].ch[1][x1]&&tr[1].ch[1][x2])dfs(tr[0].ch[1][x1],tr[1].ch[1][x2],dep-1,now);
	if(tr[0].ch[0][x1]&&tr[1].ch[1][x2])dfs(tr[0].ch[0][x1],tr[1].ch[1][x2],dep-1,now+(1<<dep));
	if(tr[0].ch[1][x1]&&tr[1].ch[0][x2])dfs(tr[0].ch[1][x1],tr[1].ch[0][x2],dep-1,now+(1<<dep));
    tr[0].pushup(x1),tr[1].pushup(x2);
}

int main()
{
	scanf("%d",&t);
	while(t--)
	   {
	   	int ans1=0,ans2=0;
	   	scanf("%d",&n);
	   	s.clear(),r.clear(),pr[0]=0,nxt[0]=1,pr[n+1]=n,nxt[n+1]=n+1;
	   	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	   	sort(a+1,a+n+1);
	   	for(int i=1;i<=n;i++)
		    {
			pr[i]=i-1,nxt[i]=i+1;
			if(i>1)s.insert({a[i-1]^a[i],{-(i-1),-i}});
		    }
		while(!s.empty())
		   {
		   int g=(*s.begin()).first,m=a[nxt[0]];
		   if(g<m||(g==m&&g>0)||(g==m&&g==0&&a[-(*s.begin()).second.second]!=0))r.push_back(g),match();
		   else break;
		   }
		while(!s.empty()&&a[pr[n+1]]==0)
		   {
		   int g=(*s.begin()).first;
		   if(g==0)r.push_back(g),match();
		   else break;
		   }
		for(int i=nxt[0];i<=n;i=nxt[i])r.push_back(a[i]);
		for(int i=0;i<(int)r.size();i++)ans1=(ans1+1ll*(r[i]+1)*(i+1)%mod)%mod;
		int d=31-__builtin_clz(a[1]^a[n]);
		if(a[1]==a[n]||(a[1]>>(d+1))>0)for(int i=1;i<=n;i++)ans2=(ans2+1ll*(a[i]+1)*i%mod)%mod;
		else
		   {
		   	rst.clear(),r.clear(),tr[0].clear(),tr[1].clear();
		   	for(int i=1;i<=n;i++)
		   	    if((a[i]>>d)&1)tr[1].insert(a[i]);
		   	    else tr[0].insert(a[i]);
		   	dfs(tr[0].rt,tr[1].rt,30,0),tr[0].find(tr[0].rt,30,0),tr[1].find(tr[1].rt,30,0);
		   	sort(r.begin(),r.end()),sort(rst.begin(),rst.end());
		   	for(int i=0;i<(int)rst.size();i++)r.push_back(rst[i]);
			for(int i=0;i<(int)r.size();i++)ans2=(ans2+1ll*(r[i]+1)*(i+1)%mod)%mod;
		   }
		printf("%d %d\n",ans1,ans2);
	   }
	return 0;
}
posted @ 2026-08-03 21:04  w9095  阅读(11)  评论(0)    收藏  举报