【过题(补题)记录】8.1

后续如果还有补题会继续补

Puzzle: Wagiri

思路其实很直观
首先将轮边抽出来
做一遍边双(去掉桥)
然后用切边将他们练成一棵树即可

我考场上用了拓扑求环,后面发现还要处理桥
改了之后一直T
赛后把Cin改成scanf就过了?
我关同步流了呀…
又是裂开的一天
这是赛时的代码,唯一区别就是把cin改成了scanf
写的确实丑…

#include<bits/stdc++.h>
using namespace std;

const int N = 4e5+100;
typedef pair < int , int > pii;
#define fi first
#define se second
vector < int > a0[N];
vector < int > a1[N];
struct Ed{
	int u,uu,v,vv;
};
vector < Ed > e[N],tr[N];
struct Node{
	int y,Next;
}ee[2*N];
#define pb push_back
int fa[N];
int n,m;
int du[N];
bool ish[N];
bool vis[N];
int len = 1, Linkk[N];
int ho[N];
int dfn[N],low[N]; 
map < pair < int , int > , bool > Mat,bri,edg;
vector < pair < int , int > > ans;

void Insertt(int x,int y){
	ee[++len] = (Node){y,Linkk[x]};
	Linkk[x] = len;
}

void Add(int x,int y,int op){
//	cout<<"OK";
	if (op == 0){
		a0[x].pb(y); a0[y].pb(x);
		Insertt(x,y); Insertt(y,x);
		du[x]++; du[y]++;
	}
	else{
//		cout<<"OK";
		a1[x].pb(y); a1[y].pb(x);
	}
}

int sz;
int cnt;

void Dfs(int x,vector <int> &ex){
	ex.pb(x);
	vis[x] = 1; sz++;
	for (int i = 0; i < a0[x].size(); i++){
		int y = a0[x][i]; if (vis[a0[x][i]]) continue;
		if (!ish[x]) continue; if (bri[{x,y}]) continue;
		Dfs(y,ex);
	}
}

void Top(){
	queue < int > q;
	for (int i = 1; i <= n; i++)
	  if (du[i] == 1) q.push(i);
//	cout<<"OK";
	while (q.size()){
//		cout<<q.size()<<endl;
		int x = q.front(); q.pop();
		ish[x] = 0;
		for (int i = 0; i < a0[x].size(); i++){
			int y = a0[x][i];
			du[y]--; if (du[y] == 1) q.push(y);
		}
	}
//	cout<<"OK";
	for (int i = 1; i <= n; i++) if (ish[i] == 1 && !vis[i]){
		vector < int > ex; sz = 0;
		Dfs(i,ex);
//		cout<<"i = "<<i<<' '<<sz<<endl;
		if (sz == 1) {ish[i] = 0;continue;}
		int bh = ex[0]+n;
		for (int j = 0; j < ex.size(); j++) ho[ex[j]] = bh;
		ex.clear();
	}
//	for (int i = 1; i <= n;i++) if (ish[i]) cout<<i<<' '<<ho[i]<<endl;
	  
}

void Add1(int lax,int lay,int nowx,int nowy){
	e[nowx].pb({lax,nowx,lay,nowy});
	e[nowy].pb({lay,nowy,lax,nowx});
}

int num[N];

int Getfa(int x){
	return fa[x] == x?x:fa[x] = Getfa(fa[x]);
}

void printh(int x){
	vis[x] = 1;
	for (int i = 0; i < a0[x].size(); i++){
		int y = a0[x][i];int x0 = x , y0 = y;
		if (ho[y] != ho[x]) continue;
		if (x0 > y0) swap(x0,y0);
		if (!Mat[{x0,y0}]) ans.pb({x,y}),Mat[{x0,y0}] = 1;
		if (!vis[y]) printh(y);
	}
}

void print(int x){
	if (x > n) printh(x-n);
	for (int i = 0; i < tr[x].size(); i++){
		Ed X = tr[x][i];
		if (X.u ==X.uu && X.v == X.vv){
			ans.pb({X.uu,X.vv});
			print(X.vv);
			continue;
		}
		if (X.u !=X.uu && X.v != X.vv){
			ans.pb({X.u,X.v});
			print(X.vv);
			continue;
		}
		if (X.u != X.uu){
			ans.pb({X.u,X.vv});
			print(X.vv);
			continue;
		}
		ans.pb({X.uu,X.v});
		print(X.vv);
	}
}

void Print(){
	int st = 1; if (ish[st]) st = ho[st];
	print(st);
	printf("YES\n%d\n",ans.size());
	for (int i = 0 ; i < ans.size(); i++){
		int x = ans[i].first , y = ans[i].second;
		if (edg[{x,y}] == 1) printf("%d %d\n",x,y);
		else printf("%d %d\n",y,x);
	}
	return ;
}

void dfs(int x){
	if (num[Getfa(x)] == cnt){
		Print(); exit(0);
	}
	for (int i = 0; i < e[x].size(); i++){
		int y = e[x][i].vv; int xx = Getfa(x) , yy = Getfa(y);
		if (xx == yy) continue;
		fa[yy] = xx; num[xx]+=num[yy];
		tr[x].pb(e[x][i]);
		dfs(y);
	}
}

int ti = 0;
void Tarjan(int x,int La){
	dfn[x] = low[x] = ++ti;
	for (int i = Linkk[x]; i; i = ee[i].Next){
		int y = ee[i].y;
		if (!dfn[y]){
			Tarjan(y,i);
			low[x] = min(low[x],low[y]);
			if (low[y] > dfn[x])
			  bri[{x,y}] = 1 , bri[{y,x}] = 1;
		}
		else if (i != (La^1))
		  low[x] = min(low[x],dfn[y]);
	}
}

int main(){
//	cin.tie(0);
//	ios::sync_with_stdio(false);
	scanf("%d %d",&n,&m);
	memset(ish,1,sizeof ish);
	for (int i = 1; i <= m; i++){
		int x,y; string s; scanf("%d %d",&x,&y); cin>>s;
		while (s.size() == 0) cin>>s;
//		if (edg[{x,y}]) continue;
		edg[{x,y}] = 1;
		if (s == "Lun") Add(x,y,0);
		else if (s == "Qie") Add(x,y,1);
	}
	for (int i = 1; i <= n; i++) if (!dfn[i]) Tarjan(i,0);
	Top();
	memset(vis,0,sizeof vis);
	for (int i = 1; i <= n; i++){
		int x = i; if (ish[i]) x = ho[i];
		if (vis[x]) continue; cnt++; vis[x] = 1;
	}
	for (int i = 1; i <= 2*n; i++) fa[i] = i,num[i] = 1;
//	cout<<"cnt = "<<cnt<<endl;
	for (int i = 1; i <= n; i++){
		int x = i; if (ish[i]) x = ho[i];
		for (int j = 0; j < a1[i].size(); j++){
			int to = a1[i][j]; int y = to; if (ish[y]) y = ho[y];
			if (x == y) continue;
			Add1(i,to,x,y);
		}
	}
	memset(vis,0,sizeof vis);
	int st = 1; if (ish[st]) st = ho[st];
	dfs(st);
	cout<<"NO";
	return 0;
}

后面用边双重新写了一遍:

#include<bits/stdc++.h>
using namespace std;

const int N = 4e5+100;
typedef pair < int , int > pii;
#define fi first
#define se second
vector < int > a1[N];
struct Ed{
	int u,uu,v,vv;
};
vector < Ed > e[N],tr[N];
struct Node{
	int y,Next;
}ee[2*N];
#define pb push_back
int n,m;
bool ish[N];
bool vis[N];
int len = 1, Linkk[N];
int ho[N];
int dfn[N],low[N]; 
map < pair < int , int > , bool > Mat;
vector < pair < int , int > > ans;

void Insertt(int x,int y){
	ee[++len] = (Node){y,Linkk[x]};
	Linkk[x] = len;
}

void Add(int x,int y,int op){
	if (op == 0){
		Insertt(x,y); Insertt(y,x);
	}
	else{
		a1[x].pb(y); a1[y].pb(x);
	}
}

int sz;
int cnt;
void Add1(int lax,int lay,int nowx,int nowy){
	e[nowx].pb({lax,nowx,lay,nowy});
	e[nowy].pb({lay,nowy,lax,nowx});
}


void printh(int x){
	vis[x] = 1;
	for (int i = Linkk[x]; i; i = ee[i].Next){
		int y = ee[i].y; int x0 = x , y0 = y;
		if (ho[y] != ho[x]) continue;
		if (x0 > y0) swap(x0,y0);
		if (!Mat[{x0,y0}]) ans.pb({x,y}),Mat[{x0,y0}] = 1;
		if (!vis[y]) printh(y);
	}
}

void print(int x){
	if (x > n) printh(x-n);
	for (int i = 0; i < tr[x].size(); i++){
		Ed X = tr[x][i];
		if (X.u ==X.uu && X.v == X.vv){
			ans.pb({X.uu,X.vv});
			print(X.vv);
			continue;
		}
		else if (X.u !=X.uu && X.v != X.vv){
			ans.pb({X.u,X.v});
			print(X.vv);
			continue;
		}
		else if (X.u != X.uu){
			ans.pb({X.u,X.vv});
			print(X.vv);
			continue;
		}
		else ans.pb({X.uu,X.v}),print(X.vv);
	}
}

void Print(){
	int st = 1; if (ish[st]) st = ho[st];
	print(st);
	printf("YES\n%d\n",ans.size());
	for (int i = 0 ; i < ans.size(); i++){
		int x = ans[i].first , y = ans[i].second;
		printf("%d %d\n",x,y);
	}
	return ;
}

bool Vi[N],br[N];
int Num;

void dfs(int x){
	Vi[x] = 1; Num++;
	if (Num == cnt){
		Print(); exit(0);
	}
	for (int i = 0; i < e[x].size(); i++){
		int y = e[x][i].vv;
		if (Vi[y]) continue;
		tr[x].pb(e[x][i]);
		dfs(y);
	}
}

int ti = 0;
void Tarjan(int x,int La){
	dfn[x] = low[x] = ++ti;
	for (int i = Linkk[x]; i; i = ee[i].Next){
		int y = ee[i].y;
		if (!dfn[y]){
			Tarjan(y,i);
			low[x] = min(low[x],low[y]);
			if (low[y] > dfn[x])
			  br[i] = br[i^1] = 1;
		}
		else if (i != (La^1))
		  low[x] = min(low[x],dfn[y]);
	}
}

void Dfs(int x,int numb){
	ho[x] = numb; sz++; ish[x] = 1;
	for (int i = Linkk[x]; i; i = ee[i].Next){
		int y = ee[i].y;
		if (ho[y] || br[i]) continue;
		Dfs(y,numb);
	}
}

int main(){
	scanf("%d %d",&n,&m);
	memset(ish,1,sizeof ish);
	for (int i = 1; i <= m; i++){
		int x,y; string s; scanf("%d %d",&x,&y); cin>>s;
		while (s.size() == 0) cin>>s;
		if (s == "Lun") Add(x,y,0);
		else if (s == "Qie") Add(x,y,1);
	}
	for (int i = 1; i <= n; i++) if (!dfn[i]) Tarjan(i,0);
	for (int i = 1; i <= n; i++) if (!ho[i]){
		sz = 0;
		Dfs(i,i+n);
		if (sz == 1) ho[i] = 0,ish[i] = 0;
	}
	memset(vis,0,sizeof vis);
	for (int i = 1; i <= n; i++){
		int x = i; if (ish[i]) x = ho[i];
		if (vis[x]) continue; cnt++; vis[x] = 1;
	}
	for (int i = 1; i <= n; i++){
		int x = i; if (ish[i]) x = ho[i];
		for (int j = 0; j < a1[i].size(); j++){
			int to = a1[i][j]; int y = to; if (ish[y]) y = ho[y];
			if (x == y) continue;
			Add1(i,to,x,y);
		}
	}
	memset(vis,0,sizeof vis);
	int st = 1; if (ish[st]) st = ho[st];
	dfs(st);
	cout<<"NO";
	return 0;
}

Challenge NPC 2

简要题意就是给你一个森林,让你求补图是否存在哈密顿路径


无解的一个充分条件显然就是菊花图
但是不是充分必要条件呢?
我们考虑一棵树(不是菊花图)
他有若干层,每一层之间的儿子之间显然是没有连边的。
这说明什么?
每一层之间的几个点可以串联成一条小哈密顿路径。
问题就变成了,我们能不能到达每一层
如果树的直径>=4,答案显然是可以的
我们可以这样子走:
找出这棵树的直径,将直径的一端作为根(其实只要层数>=4都行,为了方便直接求直径即可)
先走2,4,6……偶数层,再走1,3,5……奇数层
这样,就能不重复的经过每一层
经过每一层的同时,在将这一层的节点遍历完即可。
因此,只要不是菊花图,一定能找出一种方法,遍历这个图

但是这是一个森林。
我们可以将他们连成一棵树再按照上面的方式游走。
其中之一的方案就是将这几个树的直径一次首尾相连变成一棵树
然后再按照上面的方式游走。
这里强调直径,并不是说一定要按照直径
而是因为用直径跑出来的树的深度更深,也就不存在不符合要求的可能。
对于 n < = 3 n<=3 n<=3的情况,特判即可


#include<bits/stdc++.h>
using namespace std;

const int N = 1e6+100;
int n,m;
vector < int > a[N];
#define pb push_back
typedef pair < int , int > pii;
vector < pii > Ed;
vector < int > de[N];

#define gc getchar()
int re(){
	int s = 0; char ch = gc;
	while (ch < '0' || ch > '9') ch = gc;
	while (ch >= '0' && ch <= '9') s = s*10+ch-48 , ch = gc;
	return s;
}

bool Check(){
	for (int i = 1; i <= n; i++)
	  if (a[i].size() == n-1) {return 1;}
	return 0;
}
bool vis[N];
int d[N];

void Dfs(int x,vector < int >& ex){
	ex.pb(x); vis[x] = 1;
	for (int i = 0; i < a[x].size(); i++){
	    int y = a[x][i]; if (vis[y]) continue;
	    d[y] = d[x]+1;
	    Dfs(y,ex);
	}
}

int cnt = 0,maxd;

void dfs(int x,int dd,int faa){
	de[dd].pb(x); maxd = max(maxd,dd);
	for (int i = 0; i < a[x].size(); i++){
		int y = a[x][i]; if (y == faa) continue;
		dfs(y,dd+1,x);
	}
}

void Clear(){
	for (int i = 1; i <= maxd; i++)
	  de[i].clear();
	for (int i = 1; i <= n; i++) a[i].clear(); Ed.clear();
}

void Work(){
	n = re(); m = re();
	for (int i = 1; i <= n; i++) vis[i] = 0,d[i] = 0;
	for (int i = 1,x,y; i <= m; i++)
	  x = re() , y = re() , a[x].pb(y) , a[y].pb(x);
	if (Check()){
		printf("-1\n"); Clear();
		return ;
	}
	if (n == 2){
		Clear();
		cout<<1<<' '<<2<<endl; return;
	}
	if (n == 3){
		if (m == 0){
			Clear();
			cout<<1<<' '<<2<<' '<<3<<endl; return;
		}
		if (m == 1){
			if (a[1].size()){
				if (a[1][0] == 2) cout<<1<<' '<<3<<' '<<2<<endl;
				else cout<<1<<' '<<2<<' '<<3<<endl;
				Clear();
				return ;
			}
			Clear();
			cout<<2<<' '<<1<<' '<<3<<endl;
			return;
		}
		return ;
	}
	int La = 0;
	for (int i = 1; i <= n; i++) if (!vis[i]){
		vector < int > ex;
		Dfs(i,ex);
		int st,ed; st = i;
		for (int j = 0; j <ex.size(); j++)
		  if (d[st] < d[ex[j]]) st = ex[j];
		d[st] = 0;
		for (int j = 0; j < ex.size(); j++) vis[ex[j]] = 0;
		ex.clear();
		Dfs(st,ex);
		ed = i;
		for (int j = 0; j <ex.size(); j++)
		  if (d[ed] < d[ex[j]]) ed = ex[j];
		ex.clear();
		if (La != 0) Ed.pb({st,La});
		La = ed;
	}
	for (int i = 0; i < Ed.size(); i++){
		int x = Ed[i].first , y = Ed[i].second;
		a[x].pb(y); a[y].pb(x);
	}
	vector < int > ex; int st,ed;
	for (int i = 1; i <= n; i++) d[i] = 0,vis[i] = 0;
	Dfs(1,ex); st = 1;
	for (int i = 1; i <= n; i++) vis[i] = 0;
	for (int i = 1; i <= n; i++)
	  if (d[st] < d[i]) st = i;
	d[st] = 0;
	Dfs(st,ex); ed = st;
	for (int i = 1; i <= n; i++) if (d[ed] < d[i]) ed = i;
	maxd = 0;
	dfs(st,1,0);
	int la = 0;
	for (int i = 2; i <= maxd; i+=2){
		for (int j = 0; j < de[i].size(); j++)
		  cout<<de[i][j]<<' ',la = de[i][j];
	}
	for (int i = 1; i <= maxd; i+=2)
	  for (int j = 0; j < de[i].size(); j++)
	    cout<<de[i][j]<<' ';
	cout<<endl;
	Clear();
	return;
}

int main(){
	int t; t = re();
	while (t--) Work();
	return 0;
}
/*
5
5 2
1 5
2 3
5 4
1 2
1 3
2 4
2 5
*/
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值