ABC375

E

题目大意:

\(n\)个人,每个人初始在第\(a_i\)组,有一个力量值\(b_i\)。需要调整某些人到其他组去,使得每个组的力量值相等。若能,求出最少调整的人数。若不能,输出\(-1\)\(n\leq 100,1\leq b_i,\sum b_i \leq 1500,a_i\in\{1,2,3\}\)

分析:

暴力dfs显然不行,还要求最少调整的人数,只能是dp了呀!至于dp的状态,题目里面就只有\(a_i\)\(b_i\),显然就用\(b_i\)来设状态。设\(dp[i][x][y]\)表示考虑到第\(i\)个人,前\(i-1\)个人的第一组的力量值之和为\(x\),第二组的力量值之和为\(y\)。此处特别注意:\(i\)这一维是有必要的,不可以省略!因为转移过程为:前\(i-1\)个的和分别是\(x\)\(y\),然后考虑当前的\(i\)放在那一组后进行转移。如果没有\(i\)这一维,意味着无法体现一个一个考虑的过程了,即自己可以贡献自己,这是不行的!用滚动数组优化,复杂度\(O(n(\sum b_i) ^ 2)\)

代码:

#include <bits/stdc++.h>

using namespace std;

int n, s[4], a[105], b[105], dp[2][1505][1505];

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i ++ ) scanf("%d%d", &a[i], &b[i]), s[a[i]] += b[i], s[0] += b[i];
	if (s[0] % 3) {puts("-1"); return 0;}
	memset(dp, 0x3f, sizeof(dp)); dp[0][s[1]][s[2]] = dp[1][s[1]][s[2]] = 0;
	for (int k = 1; k <= n; k ++ )
	{
		int o = (k & 1);
		for (int i = 0; i <= 1500; i ++ )
		{
			for (int j = 0; j <= 1500; j ++ )
			{
				dp[o][i][j] = min(dp[o][i][j], dp[o ^ 1][i][j]);
				if (a[k] == 1)
				{
					if (i >= b[k] && j + b[k] <= 1500) dp[o][i - b[k]][j + b[k]] = min(dp[o][i - b[k]][j + b[k]], dp[o ^ 1][i][j] + 1);
					if (i >= b[k]) dp[o][i - b[k]][j] = min(dp[o][i - b[k]][j], dp[o ^ 1][i][j] + 1);
				}
				if (a[k] == 2)
				{
					if (i + b[k] <= 1500 && j >= b[k]) dp[o][i + b[k]][j - b[k]] = min(dp[o][i + b[k]][j - b[k]], dp[o ^ 1][i][j] + 1);
					if (j >= b[k]) dp[o][i][j - b[k]] = min(dp[o][i][j - b[k]], dp[o ^ 1][i][j] + 1);
				}
				if (a[k] == 3)
				{
					if (i + b[k] <= 1500) dp[o][i + b[k]][j] = min(dp[o][i + b[k]][j], dp[o ^ 1][i][j] + 1);
					if (j + b[k] <= 1500) dp[o][i][j + b[k]] = min(dp[o][i][j + b[k]], dp[o ^ 1][i][j] + 1);
				}
			}
		}
	}
	printf("%d\n", dp[n & 1][s[0] / 3][s[0] / 3] > n ? -1 : dp[n & 1][s[0] / 3][s[0] / 3]);
	return 0;
}

F

题目大意:

\(n\)座城市,\(m\)条无向的道路,第\(i\)条连接\(a_i\)\(b_i\),长度为\(c_i\)\(q\)次询问,分为两类。第一类:删除第\(i\)条边(永久)。第二类:求出从\(x\)\(y\)的最短路,不存在输出\(-1\)\(2\leq n \leq 300,0\leq m \leq \frac{n(n-1)}{2},1\leq c_i \leq 10 ^ {9},1\leq q \leq 2 \times 10 ^ 5\)。保证无自环重边。

分析:

经典的一个套路呀!删边不好维护,改成加边呗。要求任意两点最短路,首先floyd预处理,\(f[x][y]\)表示\(x\)\(y\)的最短路。从后往前处理每次询问。第一类,加上这条边,然后计算新的\(f\)数组。加上某条边\((a,b)\)后,\(x\)\(y\)的最短路分成三种情况:

  • 不经过这条边
  • 经过这条边,路径为\(x->a->b->y\)
  • 经过这条边,路径为\(x->b->a->y\)

特别注意:第二和三种情况都要考虑到,因为是无向边!而且这两个的结果显然不一定相等!
这三种取\(min\)即可。第二类,直接输出答案即可。

代码:

#include <bits/stdc++.h>

using namespace std;

#define ll long long

int n, m, q, cnt, v[50005], a[50005], b[50005], c[50005], p[200005][3];

ll ans[200005], f[305][305], nf[305][305];

int main()
{
	scanf("%d%d%d", &n, &m, &q);
	for (int i = 1; i <= m; i ++ ) scanf("%d%d%d", &a[i], &b[i], &c[i]);
	for (int i = 1; i <= q; i ++ )
	{
		scanf("%d", &p[i][0]);
		if (p[i][0] == 1) scanf("%d", &p[i][1]), v[p[i][1]] = 1;
		else scanf("%d%d", &p[i][1], &p[i][2]);
	}
	for (int i = 1; i <= n; i ++ )
		for (int j = 1; j <= n; j ++ )
			if (i != j)
				f[i][j] = 1e16;
	for (int i = 1; i <= m; i ++ )
		if (v[i] != 1)
			f[a[i]][b[i]] = f[b[i]][a[i]] = c[i];
	for (int k = 1; k <= n; k ++ )
		for (int i = 1; i <= n; i ++ )
			for (int j = 1; j <= n; j ++ )
				f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
	for (int i = q; i >= 1; i -- )
	{
		if (p[i][0] == 1)
		{
			for (int x = 1; x <= n; x ++ )
				for (int y = 1; y <= n; y ++ )
					nf[x][y] = min(f[x][y], min(f[x][a[p[i][1]]] + c[p[i][1]] + f[b[p[i][1]]][y], f[x][b[p[i][1]]] + c[p[i][1]] + f[a[p[i][1]]][y]));
			for (int x = 1; x <= n; x ++ )
				for (int y = 1; y <= n; y ++ )
					f[x][y] = nf[x][y];
		} 
		else ans[ ++ cnt] = (f[p[i][1]][p[i][2]] > 1e13) ? -1 : f[p[i][1]][p[i][2]];
	}
	for (int i = cnt; i >= 1; i -- ) printf("%lld\n", ans[i]);
	return 0;
}

G

题目大意:

\(n\)座城市,\(m\)条无向的道路,第\(i\)条连接\(a_i\)\(b_i\),长度为\(c_i\)\(\forall i \in [1,m]\),判断下面两个值是否相等。

  • \(1\)\(n\)的最短路的长度
  • \(1\)\(n\)不经过第\(i\)条边的最短路的长度

若是,输出\(Yes\),否则输出\(No\)\(2 \leq n \leq 2 \times 10 ^ 5,1\leq m \leq 2 \times 10 ^ 5,1\leq c_i \leq 10 ^ 9\)。保证无自环重边。所有边都可经过时,从\(1\)可以到\(n\)

分析:

首先dijkstra预处理从城市\(1\)到每座城市的最短路(记为\(d1[]\))和最短路的条数(记为\(num1[]\)),以及从城市\(n\)到每座城市的最短路(记为\(d2[]\))和最短路的条数(记为\(num2[]\))。题目即要求我们判断每条边\((a,b,c)\)是否一定在任意的最短路上。这需要同时满足两个条件:

  • 这条边在某一条最短路上,即:\(d1[a]+d2[b]+c=d1[n]\)
  • \(1\)\(a\)的最短路的条数乘上从\(b\)\(n\)的最短路的条数,等于从\(1\)\(n\)的最短路的条数。

最短路的条数,在求最短路的过程中顺便求即可。若\(d1[y] > d1[x] + z\),那么更新\(d1[y]=d1[x]+z\)的同时有\(num1[y]=num1[x]\),因为\(y\)\(x\)唯一继承而来。否则若\(d1[y] = d1[x] + z\),那么有\(num1[y]+=num1[x]\),因为到\(x\)的最短路都可以贡献到\(y\)
特别注意:因为是无向边,所以需要把\(a,b\)互换再判断一次,两次中有一次满足即输出\(Yes\)

代码:

#include <bits/stdc++.h>

using namespace std;

#define int unsigned long long

const int mod = 998244853;

priority_queue < pair < int, int > > q;

int n, m, s, tot, num[200005], nm[200005][2], d[200005][2], hd[200005], dis[200005], vis[200005], to[400005], nxt[400005], w[400005];

int read()
{
	int x = 0, fl = 1; char ch = getchar();
	while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + ch - '0'; ch = getchar();}
	return x * fl;
}

void add(int x, int y, int z)
{
	tot ++ ;
	to[tot] = y;
	nxt[tot] = hd[x];
	w[tot] = z;
	hd[x] = tot;
	return;
}

void Dij(int s)
{
	memset(vis, 0, sizeof(vis));
	memset(num, 0, sizeof(num));
	for (int i = 1; i <= n; i ++ )
		dis[i] = 1e18;
	dis[s] = 0; num[s] = 1; 
	q.push(make_pair(0, s));
	while (!q.empty())
	{
		pair < int, int > h = q.top(); q.pop();
		int x = h.second; 
		if (vis[x]) continue; vis[x] = 1;
		for (int i = hd[x]; i; i = nxt[i])
		{
			int y = to[i], z = w[i];
			if (dis[y] > dis[x] + z)
			{
				dis[y] = dis[x] + z;
				q.push(make_pair(-dis[y], y));
				num[y] = num[x];
			}
			else if (dis[y] == dis[x] + z)
			{
				num[y] = (num[y] + num[x]) % mod;
			}
		}
	}
	return;
}

signed main()
{
	n = read(); m = read(); 
	s = 1;
	for (int i = 1; i <= m; i ++ )
	{
		int x = read(), y = read(), z = read();
		add(x, y, z); add(y, x, z);
	}
	Dij(s);
	for (int i = 1; i <= n; i ++ ) d[i][0] = dis[i], nm[i][0] = num[i];
	s = n;
	Dij(s);
	for (int i = 1; i <= n; i ++ ) d[i][1] = dis[i], nm[i][1] = num[i];
	for (int i = 1; i <= 2 * m; i += 2)
	{
		int y = to[i], x = to[i + 1], z = w[i];
		if ((d[x][0] + d[y][1] + z == d[n][0]) && (1ll * nm[x][0] * nm[y][1] % mod == nm[n][0])) puts("Yes");
		else if ((d[y][0] + d[x][1] + z == d[n][0]) && (1ll * nm[y][0] * nm[x][1] % mod == nm[n][0])) puts("Yes");
		else puts("No");
	}
	return 0;
}
posted @ 2024-10-17 10:34  andysj  阅读(5)  评论(2编辑  收藏  举报