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