7.21考试总结(NOIP模拟22)[d·e·f]
你驻足于春色中,于那独一无二的春色之中。
前言
首先,这套题的暴力分数十分丰厚,大概是 81+89+30=200 。
T1 的特殊性质比较多,也都很好想,于是考场 81pts 是没有问题的。
T2 暴力求出所有点的公共的 LCA 然后,暴力上跳然后标记一下就好了。
但是,但是,我高级数据结构学傻了,先是建了一棵虚树,然后发现有些节点是不可以被删去的。。
然后我就开始把虚树上压缩了的路径在解压回来,暴力高(莫名多了一个建虚树的时间),喜挂 53pts
T3 树状数组求逆序对可以把暴力复杂度中的一个 n 降低为 logn 然后就有了 30pts。
但是,我竟然成了 CE 选手。。(第一次发现C++11不让用 rank 作为变量名)
T1 d
解题思路
对于所有的矩形,我们发现只有去掉当前长或者宽最小的才可以对答案有贡献。
因此可以枚举对于长或者宽最小的矩形去掉的数量,然后取出最优解。
但是这样的复杂度比正解多了一个 n 。
我们发现可以先把前 m 个宽最小的矩形先去掉,然后再一个一个恢复回来。
每恢复一个,就要再删除一个长最小矩形,用优先队列维护。
- 注意:之前删除过的矩形不可以再删除一边,记录一下。。
code
暴力+特殊性质81pts
#include<bits/stdc++.h>
#define int long long
#define ls x<<1
#define rs x<<1|1
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=1e5+10,INF=1e9;
int T,n,m,ans;
bool flag,vis[N];
struct Node
{
int x,y;
}s[N];
struct Node2
{
int num,id;
}a[N],b[N];
bool comp1(Node x,Node y)
{
return x.y<y.y;
}
void Sepcial_Judge1()
{
sort(s+1,s+n+1,comp1);
ans=s[m+1].x*s[m+1].y;
}
void Sepcial_Judge2()
{
int minx=INF,miny=INF;
for(int i=1;i<=n;i++)
{
minx=min(minx,s[i].x);
miny=min(miny,s[i].y);
}
ans=minx*miny;
}
void Sepcial_Judge3()
{
int maxn=0;
for(int i=1;i<=n;i++)
maxn=max(maxn,s[i].x*s[i].y);
ans=maxn;
}
bool comp2(Node2 x,Node2 y)
{
return x.num<y.num;
}
void solve()
{
ans=0;
for(int i=1;i<=n;i++)
{
a[i].num=s[i].x;
b[i].num=s[i].y;
a[i].id=b[i].id=i;
}
sort(a+1,a+n+1,comp2);
sort(b+1,b+n+1,comp2);
for(int res=0,pos,pos2;res<=m;res++)
{
pos=1;
for(int i=1;i<=res;i++)
vis[a[i].id]=true;
pos2=res+1;
for(int j=1;j<=m-res;j++)
{
while(vis[b[pos].id])
pos++;
vis[b[pos].id]=true;
}
while(vis[a[pos2].id]) pos2++;
while(vis[b[pos].id]) pos++;
ans=max(ans,a[pos2].num*b[pos].num);
for(int i=1;i<=pos2;i++)
vis[a[i].id]=false;
for(int i=1;i<=pos;i++)
vis[b[i].id]=false;
}
}
signed main()
{
T=read();
while(T--)
{
n=read();
m=read();
flag=false;
for(int i=1;i<=n;i++)
{
s[i].x=read();
s[i].y=read();
if(i!=1&&s[i-1].x!=s[i].x)
flag=true;
}
if(!flag) Sepcial_Judge1();
else if(!m) Sepcial_Judge2();
else if(m==n-1) Sepcial_Judge3();
else solve();
printf("%lld\n",ans);
}
return 0;
}
正解
#include<bits/stdc++.h>
#define int long long
#define f() cout<<"Pass"<<endl;
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=1e5+10,INF=1e9;
int T,n,m,ans;
bool flag,vis[N],v2[N];
struct Node
{
int x,y;
}s[N];
struct Node2
{
int num,id;
bool friend operator <(Node2 x,Node2 y)
{
return x.num>y.num;
}
}a[N],b[N];
priority_queue<Node2> q;
bool comp2(Node2 x,Node2 y)
{
return x.num<y.num;
}
void solve()
{
while(!q.empty()) q.pop();
memset(vis,false,sizeof(vis));
memset(v2,false,sizeof(v2));
for(int i=1;i<=n;i++)
{
a[i].num=s[i].x;
b[i].num=s[i].y;
a[i].id=b[i].id=i;
}
sort(a+1,a+n+1,comp2);
for(int i=1;i<=m;i++)
vis[a[i].id]=true;
for(int i=1;i<=n;i++)
if(!vis[b[i].id])
q.push(b[i]);
ans=a[m+1].num*q.top().num;
for(int res=m;res;res--)
{
vis[a[res].id]=false;
q.push(b[a[res].id]);
v2[q.top().id]=true;
q.pop();
if(v2[a[res].id])
continue;
ans=max(ans,q.top().num*a[res].num);
}
}
signed main()
{
T=read();
while(T--)
{
n=read();
m=read();
for(int i=1;i<=n;i++)
{
s[i].x=read();
s[i].y=read();
}
solve();
printf("%lld\n",ans);
}
return 0;
}
T2 e
解题思路
直接在树上的节点上建主席树,历史版本为每个节点的父亲。
不难发现,\(|a_i-r|\) 最小的一定是 r 或者 r的前驱和后继。
于是我们只需要查找前驱后继就好了,无非就是在大于 r 的区间中找最小值,在小于 r 的区间中找最大值。
这是第一种打法,给出 pyt 的 \(code\)
我的打法当然不同于别人了。只要可以找到比 r 小的数的个数也就是 r 的排名不久好了??
接下来就是查询区间内排名第 k 大的数就好了(这不是板子??)。
查询的时候注意一下边界问题就好了。
当然这个题不用主席树其实也可以:
发现暴力跳LCA是被第 8 个点卡住,但是线段树套平衡树套线段树是卡在了第 7 个点。
因此 暴力加树套树可做!!!(JYFHYX的 \(code\))
code
暴力跳 LCA
#include<bits/stdc++.h>
#define int long long
#define f() cout<<"Pass"<<endl;
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=1e5+10,INF=1e9;
int n,m,lca,T,typ,opt,ans,las,s[N],q[N];
int tim,dfn[N],topp[N],siz[N],fa[N],son[N],dep[N];
bool vis[N];
struct Edge
{
int tot,head[N],ver[N<<1],nxt[N<<1];
void add(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
ver[++tot]=x;
nxt[tot]=head[y];
head[y]=tot;
}
}e1,e2;
inline void dfs1(int x)
{
siz[x]=1;
for(int i=e1.head[x];i;i=e1.nxt[i])
{
int to=e1.ver[i];
if(siz[to]) continue;
dep[to]=dep[x]+1;
fa[to]=x;
dfs1(to);
siz[x]+=siz[to];
if(siz[to]>siz[son[x]])
son[x]=to;
}
}
inline void dfs2(int x,int tp)
{
dfn[x]=++tim;
topp[x]=tp;
if(son[x])
dfs2(son[x],tp);
for(int i=e1.head[x];i;i=e1.nxt[i])
{
int to=e1.ver[i];
if(dfn[to]) continue;
dfs2(to,to);
}
}
inline int LCA(int x,int y)
{
if(!x||!y) return 0;
while(topp[x]^topp[y])
{
if(dep[topp[x]]<dep[topp[y]])
swap(x,y);
x=fa[topp[x]];
}
if(dep[x]>dep[y])
swap(x,y);
return x;
}
inline void solve(int fro,int to)
{
while(fro!=to)
{
if(vis[fro]) break;
vis[fro]=true;
ans=min(ans,abs(s[fro]-opt));
fro=fa[fro];
}
vis[to]=true;
ans=min(ans,abs(s[to]-opt));
}
signed main()
{
n=read();
T=read();
typ=read();
for(int i=1;i<=n;i++)
s[i]=read();
for(int i=1,x,y;i<n;i++)
{
x=read();
y=read();
e1.add(x,y);
}
dfs1(1);
dfs2(1,1);
while(T--)
{
memset(vis,false,sizeof(vis));
vis[0]=true;
ans=INF;
opt=read();
m=read();
for(int i=1,x;i<=m;i++)
{
x=read();
q[i]=(x-1+las*typ)%n+1;
}
if(m==1)
{
las=ans=abs(opt-s[q[1]]);
printf("%lld\n",ans);
continue;
}
lca=q[1];
for(int i=2;i<=m;i++)
lca=LCA(lca,q[i]);
for(int i=1;i<=m;i++)
solve(q[i],lca);
las=ans;
printf("%lld\n",ans);
}
return 0;
}
虚树 36pts
#include<bits/stdc++.h>
#define int long long
#define f() cout<<"Pass"<<endl;
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=1e5+10,INF=1e9;
int n,m,root,rtdep,T,typ,opt,ans,las,s[N],q[N];
int top,sta[N];
int tim,dfn[N],topp[N],siz[N],fa[N],son[N],dep[N];
struct Edge
{
int tot,head[N],ver[N<<1],nxt[N<<1];
void add(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
// /*
ver[++tot]=x;
nxt[tot]=head[y];
head[y]=tot;
// */
}
}e1,e2;
void dfs1(int x)
{
siz[x]=1;
for(int i=e1.head[x];i;i=e1.nxt[i])
{
int to=e1.ver[i];
if(siz[to]) continue;
dep[to]=dep[x]+1;
fa[to]=x;
dfs1(to);
siz[x]+=siz[to];
if(siz[to]>siz[son[x]])
son[x]=to;
}
}
void dfs2(int x,int tp)
{
dfn[x]=++tim;
topp[x]=tp;
if(son[x])
dfs2(son[x],tp);
for(int i=e1.head[x];i;i=e1.nxt[i])
{
int to=e1.ver[i];
if(dfn[to]) continue;
dfs2(to,to);
}
}
int LCA(int x,int y)
{
if(!x||!y) return 0;
while(topp[x]^topp[y])
{
if(dep[topp[x]]<dep[topp[y]])
swap(x,y);
x=fa[topp[x]];
}
if(dep[x]>dep[y])
swap(x,y);
return x;
}
bool comp(int x,int y)
{
return dfn[x]<dfn[y];
}
void build(int x)
{
if(!top)
{
if(rtdep>dep[x]) root=x,rtdep=dep[x];
sta[++top]=x;
return ;
}
int lca=LCA(x,sta[top]);
while(top>1&&dep[sta[top-1]]>dep[lca])
{
// cout<<sta[top-1]<<' '<<lca<<endl;
e2.add(sta[top-1],sta[top]);
top--;
}
if(dep[lca]<dep[sta[top]])
e2.add(lca,sta[top--]);
if(!top||sta[top]!=lca)
{
sta[++top]=lca;
if(rtdep>dep[lca]) root=lca,rtdep=dep[lca];
}
sta[++top]=x;
if(rtdep>dep[x]) root=x,rtdep=dep[x];
}
void solve(int fro,int to)
{
while(fro!=to)
{
// f();
// cout<<fro<<' '<<to<<endl;
// cout<<fro<<endl;
if(!fro) break;
ans=min(ans,abs(s[fro]-opt));
fro=fa[fro];
}
ans=min(ans,abs(s[to]-opt));
}
void dfs3(int x,int fat)
{
// cout<<x<<' '<<fat<<endl;
if(!ans) return ;
for(int i=e2.head[x];i;i=e2.nxt[i])
{
int to=e2.ver[i];
if(to==fat) continue;
solve(to,x);
dfs3(to,x);
}
}
signed main()
{
// freopen("date.in","r",stdin);
n=read();
T=read();
typ=read();
for(int i=1;i<=n;i++)
s[i]=read();
for(int i=1,x,y;i<n;i++)
{
x=read();
y=read();
e1.add(x,y);
// e1.add(y,x);
}
dfs1(1);
dfs2(1,1);
// f();
/*
for(int i=1;i<=n;i++)
cout<<dfn[i]<<' ';
*/
while(T--)
{
e2.tot=top=0;
ans=rtdep=INF;
opt=read();
m=read();
memset(e2.head,0,sizeof(e2.head));
for(int i=1,x;i<=m;i++)
{
x=read();
q[i]=(x-1+las*typ)%n+1;
}
if(m==1)
{
las=ans=abs(opt-s[q[1]]);
printf("%lld\n",ans);
continue;
}
sort(q+1,q+m+1,comp);
// f();
// cout<<root<<endl;
for(int i=1;i<=m;i++)
build(q[i]);
// f();
while(--top)
{
// cout<<sta[top]<<' '<<sta[top+1]<<endl;
e2.add(sta[top],sta[top+1]);
}
// f();
// cout<<root<<endl;
dfs3(root,0);
// f();
las=ans;
printf("%lld\n",ans);
// cout<<T<<endl;
}
// f();
return 0;
}
正解
#include<bits/stdc++.h>
#define int long long
#define f() cout<<"Pass"<<endl;
#define ls tre[x].l
#define rs tre[x].r
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=5e5+10,INF=1e18;
int n,m,lca,T,typ,opt,ans,las,s[N];
int tim,dfn[N],topp[N],siz[N],fa[N],son[N],dep[N];
int all,root[N];
int cnt,lsh[N];
vector<int> v[N];
struct Edge
{
int tot,head[N],ver[N<<1],nxt[N<<1];
void add(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
ver[++tot]=x;
nxt[tot]=head[y];
head[y]=tot;
}
}e;
struct Segment_Tree
{
int l,r,dat;
}tre[N*80];
struct Ques
{
int dat,cnt;
}q[N];
inline void dfs1(int x)
{
siz[x]=1;
for(int i=e.head[x];i;i=e.nxt[i])
{
int to=e.ver[i];
if(siz[to]) continue;
dep[to]=dep[x]+1;
fa[to]=x;
dfs1(to);
siz[x]+=siz[to];
if(siz[to]>siz[son[x]])
son[x]=to;
}
}
inline void dfs2(int x,int tp)
{
dfn[x]=++tim;
topp[x]=tp;
if(son[x])
dfs2(son[x],tp);
for(int i=e.head[x];i;i=e.nxt[i])
{
int to=e.ver[i];
if(dfn[to]) continue;
dfs2(to,to);
}
}
inline int LCA(int x,int y)
{
if(!x||!y) return 0;
while(topp[x]^topp[y])
{
if(dep[topp[x]]<dep[topp[y]])
swap(x,y);
x=fa[topp[x]];
}
if(dep[x]>dep[y])
swap(x,y);
return x;
}
int insert(int pre,int l,int r,int val)
{
int x=++all,mid=(l+r)>>1;
tre[x].dat=tre[pre].dat+1;
if(l>=r) return x;
ls=tre[pre].l;
rs=tre[pre].r;
if(val<=mid) ls=insert(tre[pre].l,l,mid,val);
else rs=insert(tre[pre].r,mid+1,r,val);
return x;
}
int query_rank(int pre,int x,int l,int r,int val)
{
if(l==r) return tre[x].dat-tre[pre].dat;
int mid=(l+r)>>1;
int sum=tre[tre[x].l].dat-tre[tre[pre].l].dat;
if(val<=mid) return query_rank(tre[pre].l,tre[x].l,l,mid,val);
return sum+query_rank(tre[pre].r,tre[x].r,mid+1,r,val);
}
int query(int pre,int x,int l,int r,int rk)
{
if(l>=r) return l;
int mid=(l+r)>>1;
int sum=tre[tre[x].l].dat-tre[tre[pre].l].dat;
if(rk<=sum) return query(tre[pre].l,tre[x].l,l,mid,rk);
return query(tre[pre].r,tre[x].r,mid+1,r,rk-sum);
}
void dfs3(int x)
{
root[x]=insert(root[fa[x]],1,cnt,s[x]);
for(int i=e.head[x];i;i=e.nxt[i])
{
int to=e.ver[i];
if(to==fa[x]) continue;
dfs3(to);
}
}
signed main()
{
n=read();
T=read();
typ=read();
for(int i=1;i<=n;i++)
lsh[i]=s[i]=read();
for(int i=1,x,y;i<n;i++)
{
x=read();
y=read();
e.add(x,y);
}
dfs1(1);
dfs2(1,1);
for(int i=1,m;i<=T;i++)
{
lsh[i+n]=q[i].dat=read();
m=read();
for(int j=1,x;j<=m;j++)
{
x=read();
v[i].push_back(x);
}
}
sort(lsh+1,lsh+n+T+1);
cnt=unique(lsh+1,lsh+n+T+1)-lsh-1;
for(int i=1;i<=n;i++)
s[i]=lower_bound(lsh+1,lsh+cnt+1,s[i])-lsh;
for(int i=1;i<=T;i++)
q[i].dat=lower_bound(lsh+1,lsh+cnt+1,q[i].dat)-lsh;
dfs3(1);
for(int i=1;i<=T;i++)
{
for(int j=0;j<v[i].size();j++)
v[i][j]=(v[i][j]-1+ans*typ)%n+1;
ans=INF;
int lca=v[i][0];
for(int j=1;j<v[i].size();j++)
lca=LCA(lca,v[i][j]);
lca=fa[lca];
for(int j=0;j<v[i].size();j++)
{
int num=v[i][j],minn,maxn,temp=INF;
int rk=query_rank(root[lca],root[num],1,cnt,q[i].dat);
if(rk<=1) minn=query(root[lca],root[num],1,cnt,1);
else minn=query(root[lca],root[num],1,cnt,rk-1);
if(rk>=tre[root[num]].dat-tre[root[lca]].dat) maxn=query(root[lca],root[num],1,cnt,rk);
else maxn=query(root[lca],root[num],1,cnt,rk+1);
if(rk>=1&&rk<=tre[root[num]].dat-tre[root[lca]].dat)
temp=query(root[lca],root[num],1,cnt,rk);
ans=min(ans,min(abs(lsh[q[i].dat]-lsh[minn]),abs(lsh[maxn]-lsh[q[i].dat])));
if(temp!=INF) ans=min(ans,abs(lsh[q[i].dat]-lsh[temp]));
}
printf("%lld\n",ans);
}
return 0;
}
T3 f
解题思路
非常妙的一个题,有一个取一半的思想非常的重要。
对于 \(xor\) 的操作显然是应该拆位的,于是每一个数就会变成一个 01 串。
因此可以用 Tire 树进行维护,从而求出序列中每一位的逆序对的个数(描述的不太好,意会一下)
然后 \(xor\) 操作,直接是 0 就直接计算,是 1 的话就需要算入另一种情况的逆序对的个数。
但是,发现对于 \(2^{30}\) 的数据哪怕是 n 的复杂度也是搞不掉的,考虑把一个数"掰开"
于是我们可以发现对于大于 \(2^{\frac{k}{2}}\) 的数,其实他的前 \(\dfrac{k}{2}\) 位是在 \(2^{\frac{k}{2}}\) 已经计算过了的,因此只需要再开一个数组记录后面几位的就好了。
然后我们发现时间复杂度还满足一个 log 的空余,诶,二分答案。。
逆序对的个数上的 f 数组是满足单调性的,因此对于逆序对个数进行二分。
如果小于当前扫描值的个数的数小于 p 那么就记录答案并查询右区间。
对于二分答案函数的处理就简单许多了,首先对于之前搞得两半的 f 数组分别排一下序。
然后这样就满足单调性了,接下来双指针(此双指针非彼双指针)分别扫两个数组。
这样就可以得到符合答案的值了,对于符合的值的下标的查询也是类似。
更改一下条件就可以了,对于等于 \(f(ans)\) 的都给记录下来,然后给下标排个序就好了。
code
30pts 树状数组
#include<bits/stdc++.h>
#define int long long
#define ls x<<1
#define rs x<<1|1
#define f() cout<<"Pass"<<endl;
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=5e5+10;
int n,k,rank,s[N],q[N],a[N],f[N],id[N];
int tre[N],lsh[N],cnt;
int lowbit(int x)
{
return x&(-x);
}
void Special_Judge1()
{
printf("%lld %lld",rank-1,rank-1);
exit(0);
}
bool comp(int x,int y)
{
if(f[x]==f[y]) return x<y;
return f[x]<f[y];
}
void add(int x,int num)
{
for(int i=x;i<=n;i+=lowbit(i))
tre[i]+=num;
}
int ask(int x)
{
int sum=0;
for(int i=x;i;i-=lowbit(i))
sum+=tre[i];
return sum;
}
void Special_Judge2()
{
for(int x=0;x<(1<<k);x++)
{
memset(tre,0,sizeof(tre));
id[x]=x;
for(int i=1;i<=n;i++)
lsh[i]=q[i]=(s[i]^x)+2;
sort(lsh+1,lsh+n+1);
cnt=unique(lsh+1,lsh+n+1)-lsh-1;
for(int i=n;i>=1;i--)
{
q[i]=lower_bound(lsh+1,lsh+cnt+1,q[i])-lsh+1;
f[x]+=ask(q[i]-1);
add(q[i],1);
}
}
sort(id+0,id+(1<<k),comp);
printf("%lld %lld",f[id[rank-1]],id[rank-1]);
exit(0);
}
void solve()
{
int x=rank-1;
for(int i=1;i<=n;i++)
q[i]=s[i]^x;
for(int i=2;i<=n;i++)
for(int j=i-1;j>=1;j--)
if(q[i]<q[j])
a[i]++;
for(int i=1;i<=n;i++)
f[x]+=a[i];
printf("%lld %lld",f[x],x);
}
signed main()
{
n=read();
k=read();
rank=read();
for(int i=1;i<=n;i++)
s[i]=read();
if(k==0) Special_Judge1();
if(k<=10) Special_Judge2();
solve();
return 0;
}
正解
#include<bits/stdc++.h>
#define int long long
#define f() cout<<"Pass"<<endl;
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=5e5+10;
int n,rk,k,num,ans,s[N];
int all=1,cnt[N*40][2];
int top,sta[N*40];
struct Tire
{
int dat,ch[2];
}tre[N*40];
struct Storage
{
int dat,id;
}f1[N],f2[N];
void insert(int val)
{
int rt=1;
for(int i=k-1;i>=0;i--)
{
if(!tre[rt].ch[(val>>i)&1]) tre[rt].ch[(val>>i)&1]=++all;
cnt[i][(val>>i)&1]+=tre[tre[rt].ch[((val>>i)&1)^1]].dat;
tre[tre[rt].ch[(val>>i)&1]].dat++;
rt=tre[rt].ch[(val>>i)&1];
}
}
bool comp(Storage x,Storage y)
{
if(x.dat!=y.dat) return x.dat<y.dat;
return x.id<y.id;
}
bool judge(int val)
{
int total=0,sum=(1<<(k-k/2))-1;
for(int i=0;i<(1<<(k/2));i++)
{
if(f1[i].dat>val) break;
while(sum>=0&&f1[i].dat+f2[sum].dat>=val) sum--;
total+=sum+1;
}
if(total<rk) num=total;
return total<rk;
}
int solve()
{
int sum=(1<<(k-k/2))-1;
for(int i=0;i<(1<<(k/2));i++)
{
if(f1[i].dat>ans) break;
while(sum>=0&&f1[i].dat+f2[sum].dat>ans)
sum--;
if(f2[sum].dat+f1[i].dat==ans) sta[++top]=f2[sum].id*(1<<k/2)+f1[i].id;
}
sort(sta+1,sta+top+1);
return sta[rk-num];
}
signed main()
{
n=read();
k=read();
rk=read();
for(int i=1;i<=n;i++)
{
s[i]=read();
insert(s[i]);
}
for(int i=0;i<(1<<(k/2));i++)
{
for(int j=0;j<k/2;j++)
f1[i].dat+=cnt[j][(i>>j)&1];
f1[i].id=i;
}
for(int i=0;i<(1<<(k-k/2));i++)
{
for(int j=k/2;j<k;j++)
f2[i].dat+=cnt[j][((i>>(j-k/2))&1)];
f2[i].id=i;
}
sort(f1+0,f1+(1<<(k/2)),comp);
sort(f2+0,f2+(1<<(k-k/2)),comp);
int l=0,r=n*(n-1)/2;
while(l<=r)
{
int mid=(l+r)>>1;
if(judge(mid))
{
l=mid+1;
ans=mid;
}
else r=mid-1;
}
printf("%lld %lld",ans,solve());
return 0;
}