Luogu 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;
}

浙公网安备 33010602011771号