首页 > 其他分享 >ABC243

ABC243

时间:2025-01-15 21:23:13浏览次数:1  
标签:lfloor int ll sqrt ABC243 rfloor dp

ABC243

E

题目大意

给出一个无向连通图,在保证任两点最短路 \(d(u,v)\) 不变的情况下,最多能删多少边。

解题思路

考虑一条边满足什么条件可以删。

给出结论:当 \(d(u,v) \le w\) 且 \(d(u,v)\) 所代表的路径不经过 \((u,v)\) 时,可以删去边 \((u,v)\)

证明:当 \(d(u,v) \le w\) 时,如果有两点之间的最短路要从 \(u\) 到 \(v\),那么走 \(d(u,v)\) 所代表的路径一定不劣,因此任意两点最短路一定可以不走 \((u,v)\),此边可以删去。

现在考虑如何实现算法,观察到数据范围: \(n\le 300\),考虑使用 Floyd 算法。

  • 当 \(d(u,v) < w\) 时,只需用 Floyd 计算两点间最短路再判断大小即可。
  • 当 \(d(u,v) = w\) 且 \(d(u,v)\) 所代表的路径不经过 \((u,v)\) 时,由于此时 \(d(u,v) = w\) ,在 Floyd 松弛时如果发现松弛后与当前最短路相等(且 \(k\) 与 \(i\)、\(j\) 不相等),用一个布尔数组记录这两点间最短路不唯一即可。

代码

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

const int N = 310, M = 1e5 + 10;

struct edge
{
	int u, v;
	ll w;
} e[M];

int n, m, ans;
ll dis[N][N];
bool b[N][N];

int main()
{
	ios :: sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n >> m;
	memset(dis, 0x3f, sizeof dis);
	for (int i = 1; i <= m; i++)
	{
		cin >> e[i].u >> e[i].v >> e[i].w;
		dis[e[i].u][e[i].v] = dis[e[i].v][e[i].u] = e[i].w;
	}
	for (int k = 1; k <= n; k++)
	{
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= n; j++)
			{
				if (dis[i][j] == dis[i][k] + dis[k][j] && i != k && j != k)
				{
					b[i][j] = 1;
				}
				dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
			}
		}
	}
	for (int i = 1; i <= m; i++)
	{
		if (b[e[i].u][e[i].v] == 1 || dis[e[i].u][e[i].v] < e[i].w)
		{
			ans++;
		}
	}
	cout << ans << endl;
	return 0;
}

F

题目大意

有 \(n\) 个元素,给出每个元素被抽到的概率 \(w\),抽 \(k\) 次,求恰好抽到 \(m\) 种元素的概率。

解题思路

观察数据范围,暴力判断抽到哪些元素会超时,考虑概率 dp

  • 状态表示:\(dp[i][j][k]\) 表示考虑到第 \(i\) 个元素,抽了 \(k\) 次,有 \(j\) 种不同元素的概率。
  • 初始化:显然不抽一定会有 \(0\) 种不同,所以 \(dp[i][0][0]=1\)。
  • 状态转移:类似于多重背包,我们分两种情况讨论。
    • 不抽第 \(i\) 个元素:那么就是前 \(i-1\) 个元素,抽了 \(k\) 次,有 \(j\) 种不同元素的概率,有转移方程:

      \[dp[i][j][k]=dp[i-1][j][k] \]

    • 抽第 \(i\) 个元素:设抽了 \(x\) 个,那么抽中其他元素的概率就是 \(dp[i-1][k-x][j-1]\),抽中 \(x\) 个 \(i\) 元素的概率就是 \((\frac{w_i}{\sum w})^x\),另外我们不考虑抽中元素的顺序,因此需要再乘上 \(C_k^x\),于是有转移方程:

      \[dp[i][j][k]=dp[i-1][k-x][j-1]\cdot(\frac{w_i}{\sum w})^x\cdot C_k^x \]

    综上,将以上情况求和可得:

    \[dp[i][j][k]=dp[i-1][j][k]+\sum_{x=1}^{k} dp[i-1][k-x][j-1]\cdot(\frac{w_i}{\sum w})^x\cdot C_k^x \]

  • 答案:\(dp[n][m][k]\)。

代码实现上,可以预处理阶乘来 \(O(1)\) 计算组合数,总时间复杂度 \(O(n^4\log P)\),\(O(\log P)\)是逆元的复杂度。

代码

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

const ll N = 60, P = 998244353;

int n, m, t;
ll w[N], jc[N], sum;
ll dp[N][N][N];

inline ll qkpow(ll x, ll y)
{
	ll res = 1;
	while (y)
	{
		if (y & 1)
		{
			(res *= x) %= P;
		}
		(x *= x) %= P;
		y >>= 1;
	}
	return res;
}

inline ll c(ll x, ll y)
{
	return jc[y] * qkpow(jc[x] * jc[y - x] % P, P - 2) % P;
}

int main()
{
	ios :: sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n >> m >> t;
	for (int i = 1; i <= n; i++)
	{
		cin >> w[i];
		sum += w[i];
		dp[i][0][0] = 1;
	}
	jc[0] = 1;
	for (int i = 1; i <= t; i++)
	{
		jc[i] = (jc[i - 1] * i) % P;
	}
	dp[0][0][0] = 1;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= i; j++)
		{
			for (int k = 1; k <= t; k++)
			{
				dp[i][j][k] = dp[i - 1][j][k];
				for (int x = 1; x <= k; x++)
				{
					(dp[i][j][k] += dp[i - 1][j - 1][k - x] * c(x, k) % P * qkpow(w[i], x) % P * qkpow(qkpow(sum, x), P - 2) % P) %= P;
				}
			}
		}
	}
	cout << dp[n][m][t];
	return 0;
}

G

题目大意

给出一个序列,一开始只有 \(X\) 这一元素,设序列末尾数为 \(Y\),可以往序列末尾加小于 \(\sqrt Y\) 的数,求进行 \(10^{100}\) 次操作后有多少可能的序列。

解题思路

算法 1

发现当末尾的数为 \(1\) 时,后面的数只能为 \(1\),因此可以将多余的 \(1\) 剔除以此大大减小序列长度。

考虑线性 dp

队首元素为 \(X\),很大,难以处理,队末为 \(1\),较好处理,并且序列长度不定,不好描述。

于是便可以如此设计动态规划:

  • 状态表示:\(dp[i]\) 表示以 \(i\) 为队首的序列个数。
  • 初始化:\(dp[1]=1\)。
  • 状态转移:\(i\) 后加的数范围为 \(1\le j<\sqrt{i}\),所以有:

    \[dp[i]=\sum_{j=1}^{\lfloor\sqrt i\rfloor}dp[j] \]

    考虑用前缀和优化,记 \(s[i]\) 为 \(\sum_{j=1}^{i}dp[j]\),所以有:

    \[dp[i]=s[\lfloor\sqrt{i}\rfloor] \]

  • 答案:因为\(dp[X]=s[\lfloor\sqrt{X}\rfloor]\),所以只要遍历到 \(\lfloor\sqrt{X}\rfloor\) 即可,答案为 \(s[\lfloor\sqrt{X}\rfloor]\)

时间复杂度 \(O(\sqrt X)\),50 分。

算法 2

考虑优化算法 1,用同样的套路,变形状态转移方程:

\[\begin{aligned} dp[i]&=\sum_{j=1}^{\lfloor\sqrt i\rfloor}dp[j]\\ &=\sum_{j=1}^{\lfloor\sqrt i\rfloor}s[\lfloor\sqrt j\rfloor] \end{aligned} \]

发现 \(dp[X]\) 只由 \(\sqrt[4]{x}\) 种不同的 \(s[i]\) 组成,所以只需遍历到 \(\sqrt[4]{x}\) 即可。

考虑具体的数量关系,不难发现开根号向下取整为 \(x\) 的整数有 \(2x+1\) 个,特别地,最后一项可能凑不齐,为 \(\lfloor\sqrt X\rfloor-(\lfloor\sqrt[4] X\rfloor)^2+1\)。

所以有:

\[dp[X]=\sum_{i=1}^{\lfloor\sqrt[4] X\rfloor-1}(2i+1) \cdot s[i]+(\lfloor\sqrt X\rfloor-(\lfloor\sqrt[4] X\rfloor)^2+1)\cdot s[\lfloor\sqrt[4] X\rfloor] \]

时间复杂度 \(O(\sqrt[4] X)\),可以通过本题。

代码

#include<bits/stdc++.h>
#define endl "\n"
#define ll long long
#define ld long double
using namespace std;

const int N = 1e5;

ll t, x, n, ans;
ll dp[N], s[N];

int main()
{
	ios :: sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	dp[1] = s[1] = 1;
	for (int i = 2; i < N; i++)
	{
		dp[i] = s[(ll)sqrtl((ld)i)];
		s[i] = s[i - 1] + dp[i];
	}
	cin >> t;
	while (t--)
	{
		ans = 0;
		cin >> x;
		n = sqrtl(sqrtl((ld)x));
		for (int i = 1; i <= n - 1; i++)
		{
			ans += (2 * i + 1) * s[i];
		}
		ans += s[n] * ((ll)sqrtl((ld)x) - n * n + 1);
		cout << ans << endl;
	}
	return 0;
}

标签:lfloor,int,ll,sqrt,ABC243,rfloor,dp
From: https://www.cnblogs.com/lintianchen/p/18673721

相关文章

  • ABC243做题笔记
    AtcoderBeginnerContest243D-MovesonBinaryTree题目大意有一棵极大的二叉树,有\(2^{10^{100}}-1\)个节点,给定一些操作,输出在线段树上遍历后的最后的节点的编号。解题思路如果直接模拟,显然数据太大,会远超出longlong的范围。有一个条件非常重要:最终的答案在long......
  • AT_abc243_e [ABC243E] Edge Deletion 题解
    首先,我们可以得出一个结论:令点\(i,j\)之间的最短路径边权和\(dis_{i,j}\),若存在一个点\(k\),使得\(k\neqi\)且\(k\neqj\)且\(dis_{i,k}+dis_{k,j}=dis_{i,j}\),则连接\(i,j\)的边可以被删去。该结论的正确性是显然的,因为将连接\(i,j\)的边删去后,\(i,j\)之间的......
  • AT_abc243_g [ABC243G] Sqrt 题解
    可设\(f_i\)为以\(i\)开头的方案数,由于最后由于操作数很多所以不用考虑还剩多少次操作,显然可得状态转移方程\(f_i=\sum\limits_{j=1}^{\sqrti}f_j\),时间复杂度\(O(T+X\sqrtX)\),空间复杂度\(O(X)\),无法接受。考虑如何更优,可以发现在\(T\)次询问中,每次可以直接转移,因此......
  • AT_abc243_g [ABC243G] Sqrt题解
    题目大意有一个数列,初始时只有一个数\(X\)。你可以对它进行一种操作:设末尾的数为\(Y\),从\(1\sim\sqrt{Y}\)中选一个数加到数列的末尾。如此进行\(10^{100}\)次操作,问数列一共有多少种可能的状态。解法考虑DP。设\(dp_i\)表示以数字\(i\)开头的数列可能的状态。设......
  • ABC243F
    发现直接记录有哪些奖品被选是不可能的,所以考虑转换一下思路:设\(dp_{i,j,p}\)为只考虑前\(i\)个奖品,抽了\(j\)次,有\(p\)种不同奖品的的概率。这个状态相当于是维护一个操作(抽奖)序列。考虑每次加入\(q\)个第\(i\)种奖品,就相当于是将原序列和一个由\(q\)个\(i\)组......
  • abc243 E - Edge Deletion
    题意:给定无重边无自环的带正权无向连通图,问最多删除几条边能保持所有点对的最短距离不变\(n\le300\)思路:删除边\(u\overset{w}{-}v\)当且仅当原图中存在其它路径的......