比赛链接:
https://atcoder.jp/contests/abc243
E - Edge Deletion
题目大意:
有 \(n\) 个点,\(m\) 条边的无向图,第 \(i\) 条边长度为 \(c_i\),连接了点 \(a_i\) 和 \(b_i\)。求出最多能删除多少条边,使剩下的图中任意两点间的最短距离和给定的图相同,同时剩下的图还要是连通的。
思路:
先通过 \(Floyed\) 求出任意两点间的最短距离,接着枚举每一条边,判断这条边是不是这两个点之间的最短距离,不是的话就将这条边删除。
判断的过程就是通过枚举其它所有点,判断有没有其他路径比这条路径短,注意,相同的时候也要删除,因为其他边可能是其它的一对点之间的最短距离,而当前这一条边可以通过其他路径代替。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 310, M = 5e4 + 10;
int n, m, d[N][N], u[M], v[M], w[M];
int main(){
cin >> n >> m;
memset(d, 0x3f, sizeof d);
for (int i = 1; i <= n; ++ i)
d[i][i] = 0;
for (int i = 0; i < m; ++ i){
scanf("%d%d%d", &u[i], &v[i], &w[i]);
d[u[i]][v[i]] = w[i];
d[v[i]][u[i]] = w[i];
}
for (int k = 1; k <= n; ++ k)
for (int i = 1; i <= n; ++ i)
for (int j = 1; j <= n; ++ j)
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
int ans = 0;
for (int i = 0; i < m; ++ i)
for (int k = 1; k <= n; ++ k)
if (u[i] != k && k != v[i] && d[u[i]][k] + d[k][v[i]] <= w[i]){
ans++;
break;
}
cout << ans << "\n";
return 0;
}
G - Sqrt
题目大意:
给一个序列 \(A\),刚开始里面就一个元素 \(x\),定义这个序列的最后一个元素是 \(x\),每次可以选择 1 ~ \(\sqrt[]{x}\) 中任意一个数加入序列中,问可以形成多少个不同的序列。
思路:
原始的做法就是通过枚举 1 到 \(\sqrt[]{x}\) 所有数,记它为 \(j\),然后记忆化递归计算。O(\(\sqrt[]{x}\)) 的复杂度,超时。
尝试再分解一次,对 \(\sqrt[]{x}\) 再进行一次递归分解操作。即通过枚举 \(i\),去分解 \(j\),有 \(\sqrt[]{x} - i * i + 1\) 个 \(j\) 可以转化成同一个 \(i\),例如 5,6,7,8,最后开根号的值都是 2。复杂度降为 O(\(\sqrt[4]{x}\))。不用记忆化也能过。
ps:不要强制将 \(sqrt()\) 转成 \(long long\) 型,精度会出现问题。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
LL T = 1, x;
LL mySqrt(LL x){
LL k = sqrt(x) - 1;
while ((k + 1) * (k + 1) <= x) k++;
return k;
}
LL f(LL x){
if (x == 1LL) return 1LL;
LL ans = 0;
for (LL i = 1; i * i * i * i <= x; ++ i)
ans += f(i) * (mySqrt(x) - i * i + 1LL);
return ans;
}
void solve(){
cin >> x;
cout << f(x) << "\n";
}
int main(){
cin >> T;
while (T--)
solve();
return 0;
}