暑假集训 ---- 图论

SCU 4444 Travel
讨论:
如果 a < b a<b a<b
1 与 n 之间连的是 a a a 边,输出 a
1 与 n 之间连的是 b b b 边,对 b 做一个 0 / 1 0/1 0/1 b f s bfs bfs
如果 a > b a>b a>b
1 与 n 之间连的是 b b b 边,输出 b
1 与 n 之间连的是 a a a 边,对 a 做一个 0 / 1 0/1 0/1 b f s bfs bfs
问题就在于 b b b 边的数量太多,考虑到如果用一个点去更新其它点,每个点都做完后最多只会失配 m m m
所以我们用一个链表维护还没有被更新的点
如果当前失配了,那么总共最多 m m m
如果当前没有失配,那么会删一个点,最多 n n n

#include<bits/stdc++.h>
#define N 100050
#define M 1000050
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f; 
}
typedef long long ll;
int pre[N], suf[N];
int vis[N], out[N];
int first[N], nxt[M], to[M], tot;
void add(int x, int y){ nxt[++tot] = first[x]; first[x] = tot; to[tot] = y;}
int n, m, a, b;
ll dis[N];
const ll inf = 1e15;
void del(int x){ pre[suf[x]] = pre[x]; suf[pre[x]] = suf[x];}
void FSY(){
	queue<int> q; q.push(1); dis[1] = 0;
	memset(out, 0, sizeof(out)); out[1] = 1;
	for(int i = 0; i <= n + 1; i++) pre[i] = i - 1, suf[i] = i + 1;
	while(!q.empty()){
		int x = q.front(); q.pop();
		for(int i = first[x]; i; i = nxt[i]) vis[to[i]] = 1;
		for(int i = 0; i != n + 1; i = suf[i]){
			if(!vis[i] && !out[i]){
				dis[i] = dis[x] + b;
				q.push(i); del(i); out[i] = 1;
			}
		} 
		for(int i = first[x]; i; i = nxt[i]) vis[to[i]] = 0;
	} cout << min((ll)a, dis[n]) << '\n';
}
void Yolanda(){
	queue<int> q; q.push(1); dis[1] = 0;
	while(!q.empty()){
		int x = q.front(); q.pop();
		for(int i = first[x]; i; i = nxt[i]){
			int t = to[i]; if(dis[t] == inf){
				dis[t] = dis[x] + a; q.push(t);
			}
		}
	} cout << min((ll)b, dis[n]) << '\n'; 
}
int main(){
	while(scanf("%d%d%d%d", &n, &m, &a, &b) != EOF){
		memset(first, 0, sizeof(first)); tot = 0;
		for(int i = 1; i <= n; i++) dis[i] = inf;
		int flg = 0;
		for(int i = 1; i <= m; i++){
			int x = read(), y = read();
			add(x, y); add(y, x);
			if((x == 1 && y == n) || (x == n && y == 1)) flg = 1;
		}
		if(a == b) cout << a << '\n';
		if(a < b){ if(flg == 1) cout << a << '\n'; else Yolanda();}
		if(a > b){ if(flg == 0) cout << b << '\n'; else FSY();}
	} return 0;
}

WOJ4701 Walk
考虑在 a i ≤ 1 e 6 a_i \le 1e6 ai1e6 下文章
我们可以类似线段树优化建图一样,每个二进制数向比它只少一个1的二进制数连边,边权为 0
每个点向它的二进制数连边,边权为1,每个二进制数向点连边,边权为0
然后就是 0 / 1 b f s 0/1 bfs 0/1bfs

#include<bits/stdc++.h>
#define N 200050 + (1<<20)
#define M 20300050
using namespace std;
int first[N], nxt[M], to[M], w[M], tot;
void add(int x, int y, int z){ 
	nxt[++tot] = first[x], first[x] = tot, to[tot] = y, w[tot] = z;
}
int n, m, val[N], dis[N]; 
const int up = 1<<20;
void bfs(){
	memset(dis, 63, sizeof(dis));
	deque<int> q; q.push_front(1); dis[1] = 0;
	while(!q.empty()){
		int x = q.front(); q.pop_front(); 
		for(int i = first[x]; i; i = nxt[i]){
			int t = to[i]; if(dis[t] > dis[x] + w[i]){
				dis[t] = dis[x] + w[i];
				if(w[i] == 0) q.push_front(t);
				else q.push_back(t);
			}
		}
	} 
}
int main(){
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++) scanf("%d", &val[i]);
	for(int i = 1; i <= m; i++){
		int x, y; scanf("%d%d", &x, &y); add(x, y, 1); 
	}
	for(int i = 1; i <= n; i++) add(i, val[i] + n+1, 1), add(val[i] + n+1, i, 0);
	for(int i = 0; i < up; i++){
		for(int j = 0; j < 20; j++){
			int nxt = i | (1<<j);
			if(i == nxt || nxt >= up) continue;
			add(nxt + n+1, i + n+1, 0);
		}
	} bfs();
	for(int i = 1; i <= n; i++) printf("%d\n", (dis[i] > 1e9) ? -1 : dis[i]);
	return 0;
}

CF547D Mike and Fish
可以将行和列连边,这样子一个点的颜色就是这条边的颜色
然后一个点的出边一半是红,一半是蓝
如果将红看做入蓝看做出,那么走出来就是个欧拉回路
构造一下发现度数为奇数的点好像都是偶数个
所以如果度数是奇数的话就随便配对两两连边

#include<bits/stdc++.h>
#define N 800050
using namespace std;
int first[N], nxt[N], to[N], tot = 1;
void add(int x, int y){ nxt[++tot] = first[x], first[x] = tot, to[tot] = y;}
int vis[N], n, du[N];
void dfs(int u){
	for(int &i = first[u]; i; i = nxt[i]){
		int t = to[i]; if(vis[i] || vis[i^1]) continue;
		vis[i] = 1; dfs(t);
	}
}
int main(){
	scanf("%d", &n);
	for(int i = 1; i <= n; i++){
		int x, y; scanf("%d%d", &x, &y); y += 2e5;
		add(x, y); add(y, x); ++du[x]; ++du[y];
	} 
	int lst = 0;
	for(int i = 1; i <= 4e5; i++){
		if(du[i] & 1){
			if(lst) add(lst, i), add(i, lst), lst = 0;
			else lst = i;
		}	
	}
	for(int i = 1; i <= 4e5; i++) if(du[i]) dfs(i);
	for(int i = 1; i <= n; i++) putchar(vis[i << 1] ? 'b' : 'r');
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值