CodeForces 1361C

zszz按照传统在写题解之前是要写一堆废话的对不对

悲惨的回忆……

被卡科技了/kk,wtcl/dk

哪个神仙能想到这是个欧拉回路啊/kel

话说为啥有210个测试点啊,爪巴(

跑了整整20min了才测好一半,不等了睡觉去了,差评(

早上看AC了,原来昨天那个时候有比赛。。


先咕着,等eJOI切完了再来填坑

UPD 2020.7.24:前来填坑。

洛谷题目页面传送门 & CF题目页面传送门

给定(n)个珠子无序对,第(i)个珠子对的权值为((a_i,b_i)),每对的两个珠子已经连了起来。要求再连(n)条线,使(2n)个珠子连成简单环,其中对于每个珠子对,连接它的两条线分别与两个珠子相连。每条再连的线的权值为使得(2^x)能被它所连的两个珠子的权值的异或和整除的最大自然数(x)。你需要最大化再连的(n)条线的最小权值,并给出此时的连接方案。

(ninleft[1,5 imes10^5 ight],a_i,b_iinleft[0,2^{20} ight))

首先注意到,答案最多只有(21)种((0sim 20)),我们可以从大到小贪心地枚举答案,转最优化为判定。

考虑对于(vin[0,20]),如何判定答案是否(geq v)。注意到,要求所有再连的边的最小权值(geq v),就是所有再连的边的权值都要(geq v)。设连的两个珠子权值分别为(x,y),那么即(2^vmid xoplus y),即(xmod 2^v=ymod 2^v)。不难想到,可以将每个珠子的权值当作((a/b)_imod 2^v),这样有解当且仅当可以使得每条再连的线的两端权值相等。

这样就不难想到图论了。考虑在每个珠子对之间连边,这样最终肯定要每条边都走一遍,并回到自己,不难想到欧拉回路。那么如何体现“再连的线的两端权值相等”这个条件呢?我们把每个珠子映射到它的权值再建图即可,这样每两条相邻走过的边的交点,表示这两个珠子对连接的地方的左右两端的权值是这个同一个数。

这样,判一下,如果有欧拉回路,DFS的时候就逆序记录经过的边们连接的珠子对,最后将这个大小为(2n)的答案序列reverse即可。

时间复杂度(mathrm O((n+v)log v)),其中(v)是值域大小,(log)是枚举答案。

代码:

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define mp make_pair
#define X first
#define Y second
const int N=500000,A_I=1<<20;
int n;
int a[N+1],b[N+1];
vector<pair<int,pair<int,int> > > nei[A_I];
vector<int> reg[A_I];
vector<bool> ban[A_I];
int now[A_I];//当前弧 
vector<int> pa;
void dfs(int x=a[1]){//跑欧拉回路 
//	cout<<x<<" ";
	for(int &i=now[x];i<nei[x].size();i++)if(!ban[x][i]){
		int y=nei[x][i].X,y1=nei[x][i].Y.X,y2=nei[x][i].Y.Y;
		ban[x][i]=ban[y][reg[x][i]]=true;
		dfs(y);
		pa.pb(y2);pa.pb(y1);//逆序压入 
	}
}
void sol(int x){
	for(int i=1;i<=n;i++)a[i]%=1<<x,b[i]%=1<<x;
	for(int i=0;i<1<<x;i++)nei[i].clear(),reg[i].clear(),ban[i].clear();
	for(int i=1;i<=n;i++){//建图 
		if(a[i]==b[i]){
			nei[a[i]].pb(mp(a[i],mp(2*i-1,2*i)));nei[a[i]].pb(mp(a[i],mp(2*i,2*i-1)));
			reg[a[i]].pb(nei[a[i]].size()-1);reg[a[i]].pb(nei[a[i]].size()-2);
		}
		else{
			nei[a[i]].pb(mp(b[i],mp(2*i-1,2*i)));nei[b[i]].pb(mp(a[i],mp(2*i,2*i-1)));
			reg[a[i]].pb(nei[b[i]].size()-1);reg[b[i]].pb(nei[a[i]].size()-1);
		}
		ban[a[i]].pb(false);ban[b[i]].pb(false);
	}
	for(int i=0;i<1<<x;i++)if(nei[i].size()%2)return;//判欧拉回路 
	memset(now,0,sizeof(now));pa.clear();
	dfs(); 
//	puts("");
	if(pa.size()!=2*n)return;//图要连通 
	cout<<x<<"
";
	for(int i=2*n-1;~i;i--)printf("%d ",pa[i]);//倒序 
	exit(0);
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)scanf("%d%d",a+i,b+i);
	for(int i=20;;i--)sol(i);//从大到小枚举答案 
	return 0;
}
原文地址:https://www.cnblogs.com/ycx-akioi/p/CodeForces-1361C.html