首页 > 技术文章 > 【YBTOJ】【Luogu P3275】[SCOI2011]糖果

GJY-JURUO 2021-05-27 16:12 原文

链接:

洛谷

题目大意:

对于 \(n\) 点,点有点权,从一开始,给你 \(m\) 个相对关系。求出点权和的最小值。

\(1\leq n,m\leq 10^5\)

正文:

转化关系:

\[\begin{aligned}a_i=a_j&\rightarrow a_j\leq a_i,a_i\leq a_j\\ a_i<a_j&\rightarrow a_i< a_j\\ a_i\geq a_j&\rightarrow a_j\leq a_i\\ a_i>a_j&\rightarrow a_j< a_i\\ a_i\leq a_j&\rightarrow a_i\leq a_j\\\end{aligned}\]

然后连边,若两者之间符号是 “\(\leq\)”,边权为零,否则为一。

最后缩点跑拓扑 DP 就行了。

代码:

const int N = 1e5 + 10;

inline ll Read()
{
	ll x = 0, f = 1;
	char c = getchar();
	while (c != '-' && (c < '0' || c > '9')) c = getchar();
	if (c == '-') f = -f, c = getchar();
	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
	return x * f;
}

int n, m;

int head[N], tot;
struct edge
{
	int from, to, w, nxt;
}e[2 * N];

void Add(int u, int v, int w) {e[++tot] = (edge){u, v, w, head[u]}, head[u] = tot;} 

int dfn[N], low[N], col[N], cnt[N], stk[N], top, num, coln;

void Tarjan(int u)
{
	low[u] = dfn[u] = ++ num;
	stk[++top] = u;
	for (int i = head[u], v; i; i = e[i].nxt)
		if(!dfn[v = e[i].to])
		{
			Tarjan(v);
			low[u] = min(low[u], low[v]);
		}else
			if (!col[v])
				low[u] = min(low[u], dfn[v]);
	if (low[u] == dfn[u])
	{
		coln++;
		do
		{
			col[stk[top]] = coln;
			cnt[coln] ++;
			top--;
		}while(u != stk[top + 1]);
	}
}

ll ind[N], dis[N], ans;
queue <int> q;

void Topo()
{
	for (int i = 1; i <= coln; i++)
	{
		dis[i] = 1; 
		if(!ind[i]) q.push(i);
	}
	ans = 0;
	while (!q.empty())
	{
		int u = q.front(); q.pop();
		ans += dis[u] * cnt[u];
		for (int v, i = head[u]; i; i = e[i].nxt)
		{
			--ind[v = e[i].to];
			dis[v] = max(dis[v], dis[u] + e[i].w);
			if (!ind[v]) q.push(v);
		}
	}
}

int main()
{
	n = Read(), m = Read();
	for (int i = 1; i <= m; i++)
	{
		int opt = Read(), u = Read(), v = Read();
		if (opt == 1) Add(u, v, 0), Add(v, u, 0);
		if (opt == 2) Add(u, v, 1);
		if (opt == 3) Add(v, u, 0); 
		if (opt == 4) Add(v, u, 1);
		if (opt == 5) Add(u, v, 0);
	}
	for (int i = 1; i <= n; i++)
		if (!dfn[i]) Tarjan(i);
	
	m = tot, tot = 0;
	memset (head, 0, sizeof head);
	for (int i = 1; i <= m; i++)
		if (col[e[i].from] != col[e[i].to])
			ind[col[e[i].to]]++, Add(col[e[i].from], col[e[i].to], e[i].w);
		else if(e[i].w) {puts("-1"); return 0;} 
	
	Topo();
	
	printf ("%lld", ans);
	return 0;
}

推荐阅读