20230608 怎么全是二分图

施工中 未完待续


P4568 [JLOI2011] 飞行路线
经典分层图最短路板子
code:

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

const int N = 5000721 ;
int head[N] , nxt[N] , to[N] , v[N] , cnt ;
int dis[N] , vis[N] ;
int n , m , k , s , t ;

void cmb( int x , int y , int z ){
	to[++cnt] = y ;
	v[cnt] = z ;
	nxt[cnt] = head[x] ;
	head[x] = cnt ;
}

struct node{
	int id , dis ;
	friend bool operator < ( node a , node b ){
		return a.dis > b.dis ;
	}
};
priority_queue<node> q ;

void dijkstra( int s ){
	dis[s] = 0 ;
	q.push( (node){s,0} ) ;
	while( !q.empty() ){
		int prs = q.top().id ;
		q.pop() ;
		if( vis[prs] )
		continue ;
		vis[prs] = 1 ;
		for( int i = head[prs] ; i ; i = nxt[i] ){
			int y = to[i] ;
			if( dis[prs] + v[i] < dis[y] ){
				dis[y] = dis[prs] + v[i] ;
				q.push( (node){y,dis[y]} ) ; 
			}
		}
	}
	
}

int main () {
	
	scanf("%d%d%d" ,&n ,&m ,&k ) ;
	scanf("%d%d" ,&s ,&t ) ;
	for( int i = 1 ; i <= m ; ++i ){
		int x , y , z ;
		scanf("%d%d%d" ,&x ,&y ,&z ) ;
		for( int j = 0 ; j <= k ; ++j ){
			cmb( x + n * j , y + n * j , z ) ;
			cmb( y + n * j , x + n * j , z ) ;
		}
		for( int j = 0 ; j < k ; ++j ){
			cmb( x + n * j , y + n * ( j + 1 ) , 0 ) ;
			cmb( y + n * j , x + n * ( j + 1 ) , 0 ) ;
		}
	}
	
	if( k >= m ){
		printf("0") ;
		exit(0) ;
	}
	
	memset( dis , 0x3f , sizeof(dis) ) ;
	dijkstra(s) ;
	
	printf("%d" ,dis[t + n * k] ) ;
	
	return 0 ;
}

CF449B Jzzhu and Cities
充分诠释了什么叫题目中的每一个条件都不是白给的
首先到 1 的最短路 想到反跑 dijktra 结果这题双向边直接省了
然后发现所有的特殊边都是从 1 往外连的 那么如果删了这条边会对最短路有影响 当且仅当这条边是 1 到这个点的唯一最短路
所以我们在原最短路的基础上加一个计数 当 dis 相同时直接判最短路数量 注意删完之后最短路数量--
code:

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

const int N = 1e6 + 0721;
const int M = 1e5 + 0721;
ll dis[M];
int head[M], nxt[N], to[N], val[N], cnt;
ll sum[M];
int query[M], qdis[M];
bool vis[M];
int n, m, k;

struct node {
	ll id, dis;
	friend bool operator < (node a, node b) {
		return a.dis > b.dis;
	}
};
priority_queue<node> q;

inline void cmb(int x, int y, int z) {
	to[++cnt] = y;
	val[cnt] = z;
	nxt[cnt] = head[x];
	head[x] = cnt;
}

void dijkstra(int x) {
	memset(dis, 0x3f, sizeof dis );
	dis[x] = 0;
	q.push((node){ (ll)x, dis[x] });
	while (!q.empty()) {
		int now = q.top().id;
		q.pop();
		if (vis[now]) continue;
		vis[now] = 1;
		for (int i = head[now]; i; i = nxt[i]) {
			int y = to[i];
			if (dis[y] > dis[now] + val[i]) {
				dis[y] = dis[now] + val[i];
				sum[y] = 1;
				q.push((node) { (ll)y, dis[y] });
			} else if (dis[y] == dis[now] + val[i]) ++sum[y];
		}
	}
}

int main() {
	scanf("%d%d%d", &n, &m, &k);
	for (int i = 1; i <= m; ++i) {
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		cmb(y, x, z);
		cmb(x, y, z);
	}
	for (int i = 1; i <= k; ++i) {
		int u, y;
		scanf("%d%d", &u, &y);
		cmb(1, u, y);
		cmb(u, 1, y);
		query[i] = u;
		qdis[i] = y;
	}
	dijkstra(1);
	
	int ans = 0;
	for (int i = 1; i <= k; ++i) {
		if (dis[query[i]] < qdis[i]) ++ans;
		else if (dis[query[i]] == qdis[i] && sum[query[i]] > 1) ++ans, --sum[query[i]];
	}
	printf("%d",ans);
	
	return 0;
}

CF666B World Tour
首先需要一个全源最短路 但是 floyd 显然是会 t 掉的
发现这题边权都为 1 直接 bfs 即可 O(n2) 做掉
然后肯定是不能枚举四个点的 直接 t 飞
考虑 O(n2) 枚举两个点
然后发现 b c 两个点 可以直接涵盖到所需要的三段最短路
但是这样的话三段最短路都需要 O(1) 查询
首先 b -> c 肯定是 O(1) 的
那么对于剩下两段呢
想到预处理出到达 b 和从 c 出发路径最长的点
但是由于这题是 a -> b -> c -> d 要考虑点重复的问题
首先是不太可能与自身重复的 那么 a 就有同时与 c d 重复的可能
所以对于最长的点 我们要取前三个防止重复
另外注意 一定要保证联通才能更新答案
code:

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

const int N = 0x0d00;
const int M = 1e4 + 0721;
int head[N], to[M], nxt[M], cnt;
int dis[N][N];
int q[M], hh, tt;
bool vis[N];
int n, m;
int ans[4];
int maxx;

struct node {
	int id[4];
	int maxn[4];
} too[N], from[N];

inline void cmb(int x, int y) {
	to[++cnt] = y;
	nxt[cnt] = head[x];
	head[x] = cnt;
}

void bfs(int x) {
	memset(vis, 0, sizeof vis );
	dis[x][x] = 0;
	hh = 1, tt = 0;
	q[++tt] = x;
	while (hh <= tt) {
		int now = q[hh];
		++hh;
		vis[now] = 1;
		for (int i = head[now]; i; i = nxt[i]) {
			int y = to[i];
			if (vis[y]) continue;
			if (dis[x][y] == 0)
			dis[x][y] = dis[x][now] + 1;
			if (!vis[y])
			q[++tt] = y;
		}
	}
}

void init(int x) {
	for (int i = 1; i <= n; ++i) {
		if (dis[i][x] > too[x].maxn[1]) {
			too[x].maxn[1] = dis[i][x];
			too[x].id[1] = i;
		}
	}
	for (int i = 1; i <= n; ++i) {
		if (too[x].id[1] == i) continue;
		if (dis[i][x] > too[x].maxn[2]) {
			too[x].maxn[2] = dis[i][x];
			too[x].id[2] = i;
		}
	}
	for (int i = 1; i <= n; ++i) {
		if (too[x].id[1] == i || too[x].id[2] == i) continue;
		if (dis[i][x] > too[x].maxn[3]) {
			too[x].maxn[3] = dis[i][x];
			too[x].id[3] = i;
		}
	}
	for (int i = 1; i <= n; ++i) {
		if (dis[x][i] > from[x].maxn[1]) {
			from[x].maxn[1] = dis[x][i];
			from[x].id[1] = i;
		}
	}
	for (int i = 1; i <= n; ++i) {
		if (from[x].id[1] == i) continue;
		if (dis[x][i] > from[x].maxn[2]) {
			from[x].maxn[2] = dis[x][i];
			from[x].id[2] = i;
		}
	}
	for (int i = 1; i <= n; ++i) {
		if (from[x].id[1] == i || from[x].id[2] == i) continue;
		if (dis[x][i] > from[x].maxn[3]) {
			from[x].maxn[3] = dis[x][i];
			from[x].id[3] = i;
		}
	}
}

void work() {
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= n; ++j) {
			if (i == j) continue;
			for (int kk = 1; kk <= 3; ++kk) {
				int u = too[i].id[kk];
				if (u == i || u == j) continue;
				for (int kkk = 1; kkk <= 3; ++kkk) {
					int v = from[j].id[kkk];
					if (v == i || v == j || v == u) continue;
					if (dis[u][i] == 0 || dis[i][j] == 0 || dis[j][v] == 0) continue;
					if (maxx < dis[u][i] + dis[i][j] + dis[j][v]) {
						maxx = dis[u][i] + dis[i][j] + dis[j][v];
						ans[0] = u, ans[1] = i, ans[2] = j, ans[3] = v;
					}
//					cout << i << " " << j << " " << u << " " << v << endl;
				}
			}
		}
	}
}

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; ++i) {
		int x, y;
		scanf("%d%d", &x, &y);
		cmb(x, y);
	}
	
	for (int i = 1; i <= n; ++i) bfs(i);
	for (int i = 1; i <= n; ++i) init(i);
	work();
	
//	for (int i = 1; i <= n; ++i) {
//		for (int j = 1; j <= n; ++j) cout << dis[i][j] << " ";
//		cout << endl;
//	}
	
//	cout << maxx << endl;
	printf("%d %d %d %d",ans[0],ans[1],ans[2],ans[3]);
	
	return 0;
}

CF1268B Domino for Young
黑白染色 显然答案是 min(c1,c2) 但是我们要证明一下能取到上界
显然当 c1=c2 时肯定能铺满
我们讨论 c1c2 的情况
显然这个时候 多出来的格子如果都是一个颜色 就能取到上界了
然后对于一个杨表 如果有相邻两行长度一样的 就删去最右边那列
如果有相邻两列高度一样的 就删去最上面那行
那么如果删到不能删的情况 一定长这样
image
显然我们可以让这个玩意只空出来白格(即比如高度为偶数的列 直接密铺 对于高度为奇数的列 空出最上面一格)
进而的证
code:

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

const int N = 3e5 + 0721;
int a[N], n;
ll ans1, ans2;

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
	for (int i = 1; i <= n; ++i) {
		if (i & 1) {
			ans1 += a[i] / 2 + a[i] % 2;
			ans2 += a[i] / 2;
		} else {
			ans1 += a[i] / 2;
			ans2 += a[i] / 2 + a[i] % 2;
		}
	}
	
	printf("%lld",min(ans1, ans2));
	
	return 0;
}

CF623A Graph and String
观察一下 只有 ac 之间没有连边
那么对于原图的补图 所有的边一定都只是 ac 之间的边
然后就能想到二分图
没有连边的点就是 b
但是此时注意 原图要求所有的 ac 之间都没有连边
那么在补图中一定要满足对于每个 a 一定与每个 c 都有连边 而这个是二分图不一定满足的
所以我们要判构造完的字符串是否满足所有 ac 之间都不连边
code:

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

const int N = 521;
int _mp[N][N], mp[N][N];
int cl[N];
bool vis[N];
int n, m;
int q[N], h, t;

void bfs(int x) {
	h = 1, t= 0;
	q[++t] = x;
	while (h <= t) {
		int now = q[h];
		++h;
		for (int i = 1; i <= n; ++i) {
			if (!mp[now][i]) continue;
			if (cl[i] == -1) {
				cl[i] = !cl[now];
				q[++t] = i;
			} else if (cl[i] == cl[now]) {
				printf("No");
				exit(0);
			}
		}
	}
}

int main() {
	memset(cl, -1, sizeof cl );
	
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; ++i) {
		int x, y;
		scanf("%d%d", &x, &y);
		_mp[x][y] = _mp[y][x] = 1;
	}
	
	
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= n; ++j) {
//			cout << i << " " << j << " " << _mp[i][j] << endl;
			if (_mp[i][j] == 0 && i != j) {
				mp[i][j] = 1;
//				cout << i << " " << j << endl;
				vis[i] = vis[j] = 1;
			}
		}
	}
	
	for (int i = 1; i <= n; ++i) {
		if (!vis[i]) continue;
		if (cl[i] != -1) continue;
		cl[i] = 0;
		bfs(i);
	}
	
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= n; ++j) {
			if (_mp[i][j] && cl[i] + cl[j] == 1) {
				printf("No");
				return 0;
			}
		}
	}
	
	printf("Yes\n");
	for (int i = 1; i <= n; ++i) {
		if (cl[i] == -1) printf("b");
		else if (cl[i] == 0) printf("a");
		else printf("c");
	}
	
	return 0;
}

CF1093D Beautiful Graph
先找性质 发现 1 和 3 其实是等价的 只不过是两个不同的选择
那我们考虑每条边必须连一奇一偶 显然是个二分图
那么对于两个集合 都有可能作为奇数点的集合
假设奇数集合大小为 x 那么方案数就是 2x
当两个集合大小不同时 方案数就是 2x1+2x2
注意到图不一定保证联通 所以我们将每个连通块的方案数相乘就行
code:

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

const int N = 6e5 + 0721;
const int mod = 998244353;
int head[N], nxt[N], to[N], _cnt;
int cl[N];
int q[N], h, t;
int T;
int n, m;
int cnt[2];
ll ans;
bool fail;

inline void cmb(int x, int y) {
	to[++_cnt] = y;
	nxt[_cnt] = head[x];
	head[x] = _cnt;
}

void bfs(int x) {
	cnt[0] = cnt[1] = 0;
	cl[x] = 0;
	++cnt[0];
	h = 1, t = 0;
	q[++t] = x;
	while (h <= t) {
		int now = q[h];
		++h;
		for (int i = head[now]; i; i = nxt[i]) {
			int y = to[i];
			if (cl[y] == -1) {
				cl[y] = !cl[now];
				++cnt[cl[y]];
				q[++t] = y;
			} else if (cl[y] == cl[now]) {
				fail = 1;
				return;
			}
		}
	}
}

int main(){
	
	scanf("%d", &T);
	while (T--) {
		ans = 1;
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= n; ++i) cl[i] = -1;
		for (int i = 1; i <= n; ++i) head[i] = 0;
		_cnt = 0;
		fail = 0;
		for (int i = 1; i <= m; ++i) {
			int x, y;
			scanf("%d%d", &x, &y);
			cmb(x, y);
			cmb(y, x);
		}
		for (int i = 1; i <= n; ++i) {
			if (fail) break;
			if (cl[i] != -1) continue;
			bfs(i);
			if (fail) break;
			ll tmp1 = 1, tmp2 = 1;
			for (int j = 1; j <= cnt[0]; ++j) {
				tmp1 <<= 1;
				tmp1 %= mod;
			}
			for (int j = 1; j <= cnt[1]; ++j) {
				tmp2 <<= 1;
				tmp2 %= mod;
			}
			tmp1 = (tmp1 + tmp2) % mod;
			ans = ans * tmp1 % mod;
		}
		if (fail) printf("0\n");
		else printf("%lld\n",ans);
	}
	
	return 0;
}
posted @   Steven24  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示