专题训练 1102(图论与数据结构)(未完成)

题目均为人工翻译英文,不清楚的地方 请 连蒙带猜 结合样例理解
ABC来自atcoder, DEF来自codeforces
震哥写的题解
校内链接,时限开了三年,各位同学可以继续做的鸭

在这里插入图片描述

T1 A

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

const int N = 1e5+5;
const int INF = 1e8;//这里不可以设成1e9啊啊啊
int n, cnt=0;
ll a[N];
int dis[205][205], e[205][205];

int main() {
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) {
		ll x;
		scanf("%lld", &x);
		if(x) a[++cnt] = x;//如果为0,无边
	}
	if(cnt >= 130) { puts("3"); return 0;}
	//边界值
	for(int i = 1; i <= cnt; i++)
		for(int j = 1; j <= cnt; j++) 
			if((a[i]&a[j]) && (i!=j)) e[i][j] = dis[i][j] = 1;
			else e[i][j] = dis[i][j] = INF;
	//赋初值
    int ans = INF;
    for(int k = 1; k <= cnt; k++) {
        for(int i = 1; i < k; i++)
            for(int j = i+1; j < k; j++)
        		ans = min(ans, dis[i][j] + e[i][k] + e[k][j]);
        for(int i = 1; i <= cnt; i++)
            for(int j = 1; j <= cnt; j++)
                dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
    }
    //Floyed求最小环长度
    ans>cnt ? puts("-1") : printf("%d\n", ans);
	return 0;
}

在这里插入图片描述
在这里插入图片描述

T2 B

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

T3 C

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

T4 D

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这题很清真啊~
只要注意一下时间上的优化,用 d e e p [ i ] deep[i] deep[i]记录第 i i i号点下面深度为 d e e p [ i ] deep[i] deep[i]的所有点都已经染色
在这里插入图片描述

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

const int N = 1e5+10;
struct qyx{int num, d, ans;} p[N];
int vis[N], deep[N];
int len=0, cnt=0, sum, n, m, Q;

vector<int> e[N];//不知道为什么领接表会RE……就用Vector辽

void Insert(int a, int b, int c) {
	p[++cnt].num = a;
	p[cnt].d = b;
	p[cnt].ans = c;
}

void dfs(int k, int d, int ans, int fa) {
	if(deep[k]>d) return;
	deep[k] = d;//这是一个时间上优化,记录搜索的深度
	if(!vis[k]) vis[k] = ans, sum--;//如果颜色没有被改过
	for(int i = 0; i<e[k].size(); i++) {
		int y = e[k][i];
		if(y == fa) continue;
		if(d) dfs(y, d-1, ans, k);
	}
}

int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1, v, u; i <= m; i++) {
		scanf("%d%d", &u, &v);
		e[v].push_back(u);
		e[u].push_back(v);
	}
	scanf("%d", &Q);
	for(int i = 1,u, v, w; i <= Q; i++) {
		scanf("%d%d%d", &u, &v, &w);
		Insert(u, v, w);
	}
	sum = n;
	//倒着寻找颜色,因为颜色是会覆盖的
	for(int i = Q; i >= 1&&sum; i--) 
		dfs(p[i].num, p[i].d, p[i].ans, 0);	
	for(int i = 1; i <= n; i++)
		printf("%d\n", vis[i]);
	return 0;
}

在这里插入图片描述
在这里插入图片描述

T5 E

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

T6 F

在这里插入图片描述
在这里插入图片描述
这题也清真~
但是我交了十几遍……是我太愚蠢……INF不能开到1e9,因为在Floyed中会爆
在这里插入图片描述

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

const int N = 1e5+5;
const int INF = 1e8;//这里不可以设成1e9啊啊啊 
int n, cnt=0;
ll a[N];
int dis[205][205], e[205][205];

int main() {
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) {
		ll x;
		scanf("%lld", &x);
		if(x) a[++cnt] = x;
	}
	if(cnt >= 130) { puts("3"); return 0;}
	//因为与的性质,一旦不为0的数超过130,必然有三个数在同一位上皆为1(鸽巢原理),那么必然存在最小环3
	//边界
	for(int i = 1; i <= cnt; i++)
		for(int j = 1; j <= cnt; j++) 
			if((a[i]&a[j]) && (i!=j)) e[i][j] = dis[i][j] = 1;
			else e[i][j] = dis[i][j] = INF;
    int ans = INF;
    for(int k = 1; k <= cnt; k++) {
        for(int i = 1; i < k; i++)
            for(int j = i+1; j < k; j++)
        		ans = min(ans, dis[i][j] + e[i][k] + e[k][j]);
        for(int i = 1; i <= cnt; i++)
            for(int j = 1; j <= cnt; j++)
                dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
    }
    //Floyed求最小环
    ans>cnt ? puts("-1") : printf("%d\n", ans);
	return 0;
}

在这里插入图片描述
在这里插入图片描述

T7 G

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这道题目超级巧妙的说,其实只要能想到怎么建图,一切就很清真辽!
建图之前,在最外面框一圈0,,为什么呢,下面说。
我们把一种颜色看成一个点,只要与它相邻就建边,我们只考虑上下左右四个方向(因为题目中排除了对角线的情况),但只遍历右下两个方向,避免连边太多,因为左上的情况,它左上的点的连线会连向它。这很显然的叭!
这样就建成了一颗假的树(会出现环的呢),仔细观察一下,发现如果一个点 x x x 是割点,这意味着什么呢,如果 x x x 的子树和 x x x 断开了联系,就与最外层的 0 0 0 隔绝,也就意味着 x x x 的子树所代表的所有颜色都被 x x x 包裹在里面,太好了,答案已经出来了。
首先进行建图。
然后 d f s dfs dfs 计算子树大小,这时候最外圈的 0 0 0 就有用啦! 0 0 0在最外面,自然就是那个年纪最大的祖先!
然后遍历求割点,这里有一个坑!我刚开始就判断了一个点 x x x 是否是割点,如果是就输出 s i z e [ x ] − 1 size[x]-1 size[x]1,否则输出 0 0 0,但是这是不正确的,只能过 9 9 9 个点(这可能是因为我 R P RP RP 比较高,竟然能过 9 9 9 个点)。因为这棵树是个假的树,它存在环,很有可能你求出的割点 x x x,分割出来的子树加上 x x x 就是一个环或者子树直接与 0 0 0 有连边,这个时候,这棵子树是不可以被加进答案里的,所以我们要判的其实是割边,如果是割边,就加入这个割边所相连的子树。
好了,做完了!
在这里插入图片描述

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

const int N = 1000100;
struct psx{int y, next;} e[N<<3];
int lin[N], size[N], vis[N], dfn[N], low[N], cnt[N];
int len=0, num, sum, root, n, m;
int a[1010][1010];
void add(int yy, int xx) {
	e[++len].next = lin[xx];
	lin[xx] = len;
	e[len].y = yy;
}

void dfs(int k) {
	if(k > num) num = k;
	size[k] = vis[k] = 1;
	for(int i = lin[k]; i; i = e[i].next) {
		int y = e[i].y;
		if(vis[y]) continue;
		dfs(y);
		size[k] += size[y];
	}
}

void tarjan(int x) {
	dfn[x] = low[x] = ++sum;
	for(int i = lin[x]; i; i = e[i].next) {
		int y = e[i].y;
		if(!dfn[y]) {
			tarjan(y);
			low[x] = min(low[x], low[y]);
			if(low[y] >= dfn[x]) cnt[x] += size[y];	
		}
		else low[x] = min(low[x], dfn[y]);
	}
}

int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++)
			scanf("%d", &a[i][j]);
	for(int i = 0; i <= n; i++)
	for(int j = 0; j <= m; j++) {
		if(a[i][j] != a[i+1][j]) add(a[i][j], a[i+1][j]),add(a[i+1][j], a[i][j]);
		if(a[i][j] != a[i][j+1]) add(a[i][j], a[i][j+1]),add(a[i][j+1], a[i][j]);
 	} //建图
 	dfs(0);//求子树
 	tarjan(0);//求割点
 	for(int i = 1; i < num; i++)
 		printf("%d ", cnt[i]);
 	printf("%d\n", cnt[num]);
	return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值