题解:AT_abc252_h [ABC252Ex] K-th beautiful Necklace
include13_fAKe · · 题解
怎么有人这题写了
第一个要考虑的问题是答案的上限。根据小学奥数,要把一个数拆成若干个等于它的数,应当尽最大努力拆
将
考虑根号分治,把数分成两个大小接近
如果朴素二分答案,会发现是
-
实现细节:
- 考虑到在字典树上会遍历到空节点,所以如果不想特判就要从
1 开始排序。 - 尽量不要使用
\texttt{1<<i} ,会挂。
- 考虑到在字典树上会遍历到空节点,所以如果不想特判就要从
#include<bits/stdc++.h>
#define int long long
#define mp(a,b) make_pair(a,b)
using namespace std;
inline int read(){
int a=0,b=1;
char c=getchar();
while(!isdigit(c)){
if(c=='-') b=-1;
c=getchar();
}
while(isdigit(c)){
a=(a<<1)+(a<<3)+(c-'0');
c=getchar();
}
return a*b;
}
inline void write(int x){
if(x<0) putchar('-'),x=-x;
if(x>=10) write(x/10);
putchar(x%10+48);
}
inline void write1(int x){
write(x),putchar(' ');
}
inline void write2(int x){
write(x),putchar('\n');
}
int n,col,k; //代表宝石总数 颜色数 第 k 大
//枚举 前 k 大的分数线 字典树二分实现即可
int colin[509]; //谭总的世界 IOI2025 金牌线
vector<int> g[78];
struct node{
int sum;
int id;
friend bool operator <(node a,node b){
return a.sum>b.sum;
}
}col1[509];
int C[509]; //属于第一类还是第二类
int a[1919810],b[1919810]; //把 a 全部插入 Trie 让 b 在 Trie 上跑
int aidx=0,bidx=0; //代表目前的情况
int pow2[78];
void dfs1(int now,int xor_){
// cout<<'#'<<now<<' '<<xor_<<endl;
if(now==col+1){
a[++aidx]=xor_;
// cout<<'$'<<aidx<<' '<<xor_<<endl;
return;
}
if(C[now]==2){
dfs1(now+1,xor_);
return;
}
for(int i=0;i<g[now].size();i++){
int x=g[now][i];
dfs1(now+1,xor_^x);
}
}
int b1[1919810];
void dfs2(int now,int xor_){
// cout<<'!'<<now<<' '<<xor_<<endl;
if(now==col+1){
b[++bidx]=xor_,b1[bidx]=1; //代表初始在 1 号点
return;
}
if(C[now]==1){
dfs2(now+1,xor_);
return;
}
for(int i=0;i<g[now].size();i++){
int x=g[now][i];
dfs2(now+1,xor_^x);
}
}
const int M=3e7+15;
struct tree{
int tot;
int lson,rson; //代表两边的答案 规定 rson
}Trie[M];
int Trie_idx=1; //代表目前的解 以一为根
//int b1[1919810];
void insert(int sum){
int now=1; //目前所在节点
Trie[1].tot++; //代表这就是目前节点
for(int i=59;i>=0;i--){
// cout<<'*'<<sum<<endl;
if(sum>=pow2[i]){
// cout<<'!'<<' '<<i<<' '<<sum<<' ';
sum-=pow2[i];
// cout<<sum<<endl;
if(Trie[now].rson==0) Trie[now].rson=++Trie_idx;
// Trie[now].tot++;
now=Trie[now].rson;
Trie[now].tot++;
}
else{
if(Trie[now].lson==0) Trie[now].lson=++Trie_idx;
now=Trie[now].lson;
Trie[now].tot++;
}
}
// cout<<"终点结构"<<' '<<now<<endl;
}
void init(){
pow2[0]=1;
for(int i=1;i<=59;i++) pow2[i]=pow2[i-1]*2;
}
signed main(){
init();
// for(int i=1;i<=59;i++){
//
// }
// cout<<endl;
// write(558);
n=read(),col=read(),k=read();
for(int i=1;i<=n;i++){
int col1=read(),a=read();
g[col1].push_back(a);
colin[col1]++; //或许改一种方式更好
}
for(int i=1;i<=col;i++){
col1[i]=(node){colin[i],i};
}
sort(col1+1,col1+col+1); //代表之后还要分组 !
int a_=1,b_=1; //分组的处理法!
for(int i=1;i<=col;i++){
int x=col1[i].sum,id=col1[i].id;
if(a_>b_){
b_*=x;
C[id]=2;
}
else{
a_*=x;
C[id]=1;
}
}
// for(int i=1;i<=col;i++){
// cout<<'!'<<C[i]<<endl;
// }
dfs1(1,0); //代表目前的情况
dfs2(1,0);
// cout<<aidx<<endl;
// for(int i=1;i<=aidx;i++){
// cout<<a[i]<<' ';
// }
// cout<<endl;
// cout<<bidx<<endl;
// for(int i=1;i<=bidx;i++){
// cout<<b[i]<<' ';
// }
// cout<<endl;
for(int i=1;i<=aidx;i++){
insert(a[i]);
}
// for(int i=1;i<=Trie_idx;i++){
// cout<<'^'<<i<<' '<<Trie[i].lson<<' '<<Trie[i].rson<<' '<<Trie[i].tot<<endl;
// }
// cout<<endl;
//考虑 k 的特性
int include13=0;
// cout<<"谭总的世界-040"<<endl;
for(int i=59;i>=0;i--){
//确定答案 !
// if(i<=4) cout<<"谭总的世界-041"<<endl;
int cal=0; //代表 取 1 的情况数
for(int j=1;j<=bidx;j++){
if(b[j]&pow2[i]){
//代表选 0 的一方
cal+=Trie[Trie[b1[j]].lson].tot;
}
else cal+=Trie[Trie[b1[j]].rson].tot;
// if(i<=4){
// cout<<cal<<' ';
// }
}
// cout<<endl;
// cout<<'#'<<cal<<' '<<k<<endl;
if(cal<k){ //代表能够凑出这一位的方案小于等于 k 种 这里加不上去!
k-=cal;
// cout<<'A';
for(int j=1;j<=bidx;j++){
if(b[j]&pow2[i]){
//往同向方向走
b1[j]=Trie[b1[j]].rson;
}
else b1[j]=Trie[b1[j]].lson; //走路上朝!
// cout<<b1[j]<<' ';
}
// cout<<endl;
}
else{
// cout<<'B';
for(int j=1;j<=bidx;j++){
if(b[j]&pow2[i]) b1[j]=Trie[b1[j]].lson;
else b1[j]=Trie[b1[j]].rson;
// cout<<b1[j]<<' ';
}
// cout<<endl;
include13+=pow2[i];
}
// cout<<'&'<<include13<<endl;
// cout<<"谭总的世界-017"<<endl;
// for(int j=1;j<=bidx;j++){
// cout<<b1[j]<<' ';
// }
// cout<<endl;
}
// cout<<include13<<endl;
write2(include13);
// for(int i=1;i<=bidx;i++){
// cout<<b[i]<<' ';
// }
// cout<<endl;
return 0;
}//一定要挽回罗琪钧的遗憾啊,一定会挽回罗琪钧的遗憾的!