bzoj 2330 / AcWing 368 银河 差分约束系统+tarjan缩点+拓扑排序

怎么最近bzoj一直上不了,莫非是挂了?

AcWing的地址:https://www.acwing.com/problem/content/370/

题意:

银河中的恒星浩如烟海,但是我们只关注那些最亮的恒星。

我们用一个正整数来表示恒星的亮度,数值越大则恒星就越亮,恒星的亮度最暗是 1。

现在对于 N 颗我们关注的恒星,有 M 对亮度之间的相对关系已经判明。

你的任务就是求出这 N 颗恒星的亮度值总和至少有多大。

 

这代码只在AcWing上提交过

听说这道题用spfa能水过? 反正我试了一下就算加了快读也超时。。

首先很明显是一个差分约束系统的题目,转化为不等式的关系

构建边使得沿着边走亮度不减,也就是说从较小的向较大的连边

假如A的亮度小于B的亮度,那么添加一条从A到B权值为1的边

假如A的亮度不小于B的亮度,那么添加一条从B到A权值为0的边

假如A,B亮度相等,添加A到B和B到A权值为0的边

 

因为恒星最暗为1,从0到每一个点连接一条权值为1的边,初始化点0的亮度为0

 

因为这道题数据量为1e5,spfa的时间复杂度好像是nm,会超时

当图出现这种情况的时候:

只有A,B,C相互的边权都为0才成立,假如此时有任何一条边权为1,都会死循环

而当ABC的边权都为0,说明ABC的亮度一样,此时可以将ABC缩点,看成一个点,最后对答案的贡献就是这个点的亮度*点的大小

因此需要tarjan求强连通分量

缩点完成之后按拓扑排序计算每个点的最小亮度,最后累加答案即可

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 7;
int Head[maxn], Nxt[maxn << 2], To[maxn << 2], Val[maxn << 2]; //建边
int dfn[maxn], low[maxn]; //tarjan用的
int n, m, tot, id;
vector<int> blo[maxn]; //block,保存每一个大点,不过这道题似乎不需要这个orz
int blo_id, st_ind, blo_tot; //blo_id: block_id 记录大点编号  st_ind:stack_index,栈的指针  blo_tot:block_tot,缩点之后建边用的
int belong[maxn], st[maxn], ins[maxn]; //belong[i]:点i属于哪个大点,st:stack  , ins:in_stack,点i是否在栈中
int blo_Head[maxn], blo_To[maxn << 2], blo_Val[maxn << 2], blo_Nxt[maxn << 2];//缩点之后再建边
int blo_deg[maxn]; //block_deg:大点的入度
void add_edge(int fro, int to,int val) {
	Nxt[++tot] = Head[fro];
	To[tot] = to;
	Head[fro] = tot;
	Val[tot] = val;
}
void add_blo_edge(int fro, int to, int val) {
	blo_Nxt[++blo_tot] = blo_Head[fro];
	blo_To[blo_tot] = to;
	blo_Val[blo_tot] = val;
	blo_Head[fro] = blo_tot;
}
void tarjan(int now){
	dfn[now] = low[now] = ++id;
	st[++st_ind] = now; ins[now] = 1;
	for (int i = Head[now]; i; i = Nxt[i]) {
		const int &to = To[i];
		if (!dfn[to]) {
			tarjan(to);
			low[now] = min(low[now], low[to]);
		}
		else if (ins[to]) low[now] = min(low[now], dfn[to]);
	}
	if (dfn[now] == low[now]) {
		int z;
		blo_id++;
		do {
			z = st[st_ind--];
			ins[z] = 0;
			belong[z] = blo_id;
			blo[blo_id].push_back(z);
		} while (z != now);
	}
}
queue<int> Q;
int dis[maxn];
void top_sort() {
	for (int i = 1; i <= blo_id; i++) if (!blo_deg[i]) Q.push(i);
	while (!Q.empty()) {
		int now = Q.front(); 
		Q.pop();
		for (int i = blo_Head[now]; i; i = blo_Nxt[i]) {
			const int &to = blo_To[i], &val = blo_Val[i];
			//缩点之后保证无环
			dis[to] = max(dis[to], dis[now] + val);
			if (--blo_deg[to] == 0) Q.push(to);
		}
	}
}
int main() {
	cin >> n >> m;
	int fro, to, opt;
	for (int i = 1; i <= m; i++) {
		scanf("%d %d %d", &opt, &fro, &to);
		if (opt == 1) add_edge(fro, to, 0), add_edge(to, fro, 0);
		else if (opt == 2) {
			if (fro == to) {
				printf("-1\n");
				return 0;
			}
			add_edge(fro, to, 1);
		}
		else if (opt == 3) add_edge(to, fro, 0);
		else if (opt == 4) {
			if (to == fro) {
				printf("-1\n");
				return 0;
			}
			add_edge(to, fro, 1);
		}
		else add_edge(fro, to, 0);
	}
	for (int i = 1; i <= n; i++) add_edge(0, i, 1);
	tarjan(0);
	for (int now = 0; now <= n; now++) {
		for (int i = Head[now]; i; i = Nxt[i]) {
			int &to = To[i];
			if (belong[now] == belong[to]) { 
				if (Val[i] == 1) {
					printf("-1\n");
					return 0;
				}
				continue;
			}
			add_blo_edge(belong[now], belong[to], Val[i]);
			blo_deg[belong[to]]++;
		}
	}
	top_sort();
	long long ans = 0;
	for (int i = 1; i <= blo_id; i++) {
		ans += 1LL * dis[i] * blo[i].size();
	}
	cout << ans << endl;
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值