題意:
有n個珍珠,每個珍珠有兩個頭,每個頭都有一個值,如果兩個珍珠的兩個頭相連的話,就會得到一個值k:假設兩個值是a,b,k就是a^b之後的二進制位上一個1的位置,如果是a=b,那麼k=20
現在要將這些珍珠連成一個圓,並且你的答案是所有得到的值中最小的值,問你這個值最大是多少。
題解:
這道題對我來說是有點難了,因爲我基本上不碰圖論的題目,首先肯定是從大到小枚舉k,因爲只有20,然後將數劃分爲種情況,一開始我是想要找一個哈密頓迴路,但是它的時間好像是的,於是這道題目宣告破產。
然後去看cf大神們的代碼,發現是可以直接dfs,在回溯的時候將數放到答案中,這很神奇,我也想了一會才能理解。
首先要判斷每種情況的珍珠的數量是否是偶數,如果不是的話,那說明無法構成歐拉回路。否則的話可以直接dfs,因爲珍珠間連邊的話,一定是值&之後是相同的才能連邊,於是就算找到一條死路也無妨,由於是回溯的時候將數放到答案中,所以可以看成是從死路來的,舉個例子,就拿第7個樣例來舉例:
5
5 1
5 0
5 10
6 6
10 2
假設現在k=1
首先進去的是,然後由於有另一端,所以到了2,2找到了3,3必須連到4,4找到了6,6必須找5,然後發現是死路,退回到6,由於6必須連5,所以退回到了4,找到了7,然後是8,然後是9,10
那麼最後答案是5,6,10,9…,2,1
就可以發現,就算走到5的時候將與他相等的數用光了也沒有關係,由於每種情況都是偶數所以前面一定有一個與他相等的,然後在回退的時候爲什麼能保證與6相連的一定是和他相等的數呢,因爲4與6相等,之後4連得邊有一定與4相等,比如這裏找到了7,那麼7與6就是可以相鄰的。
有丶東西
我用的是unordered_map,時間複雜度較差,可以考慮不用
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int link[N];
int n;
unordered_map<int,vector<int> >Q;
int vis[N],a[N];
int cal(int x,int y){return x-((x>>y)<<y);}
vector<int>vec;
void dfs(int x,bool f,int bit){
vis[x]=1;
if(f)
dfs(link[x],0,bit);
else{
while(1){
int v=cal(a[x],bit);
while(Q[v].size()&&vis[Q[v].back()])
Q[v].pop_back();
if(Q[v].empty())
break;
int ne=Q[v].back();
Q[v].pop_back();
dfs(ne,1,bit);
}
}
vec.push_back(x);
}
int check(int bit){
vec.clear();
Q.clear();
memset(vis,0,sizeof(vis));
for(int i=1;i<=n*2;i++)
Q[cal(a[i],bit)].push_back(i);
for(int i=1;i<=n*2;i++)
if(Q[cal(a[i],bit)].size()%2)
return 0;
dfs(1,1,bit);
return vec.size()==n*2;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i*2-1],&a[i*2]);
link[2*i-1]=2*i;
link[2*i]=2*i-1;
}
int ans=20;
while(ans){
if(check(ans))
break;
ans--;
}
printf("%d\n",ans);
check(ans);
for(auto i:vec)
printf("%d ",i);
printf("\n");
return 0;
}