CF
1359E - Modular Stability(组合数学 *2000)
- 这题应该是能出的,玩样例的时候脑子抽了玩错了(
- 稍微看几组例子就能发现,最终的答案是由最小的数 a 1 a_1 a1 决定的,之后的数都要是 a 1 a_1 a1 的倍数才行
- 所以枚举每一个 a 1 a_1 a1 ,答案加上 C n i − 1 k − 1 C_{\frac{n}{i}-1}^{k-1} Cin−1k−1
#include <bits/stdc++.h>
using namespace std;
#define int long long
using i64 = long long;
typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;
const int N = 1e6 + 10;
const int maxn = 1e6 + 10;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;
int Jc[maxn];
void calJc() //求 maxn 以内的数的阶乘 不知道开多少就1e6吧爆不了
{
Jc[0] = Jc[1] = 1;
for(int i = 2; i < maxn; i++) Jc[i] = Jc[i - 1] * i % mod;
}
int pow(int a, int n, int p) // 快速幂取模
{
int ans = 1;
while (n)
{
if (n & 1) ans = ans * a % p;
a = a * a % p;
n >>= 1;
}
return ans;
}
int niYuan(int a, int b) //费马小定理求逆元
{
return pow(a, b - 2, b);
}
int C(int a, int b) // 组合数
{
if(a < b) return 0;
return Jc[a] * niYuan(Jc[b], mod) % mod * niYuan(Jc[a - b], mod) % mod;
}
void solve()
{
calJc();
int n, k;
cin >> n >> k;
if (n < k) cout << 0 << '\n';
else
{
int ans = 0;
for (int i = 1; i <= n - k + 1; i ++ ) // 枚举最小的数
{
ans = (ans + C(n / i - 1, k - 1)) % mod;
}
cout << ans << '\n';
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
// cin >> t;
while (t--)
{
solve();
}
}
1399E1 - Weights Division (easy version) (dfs+堆 *2000)
- 对于一条边的贡献,很容易知道是边权乘使用次数,使用次数就是这条边下面的叶子结点的个数,这个我们用dfs就可以处理出来了
- 然后用堆,但是这里有一个易错点!堆里的排序规则应该是, ( w − w 2 ) × c n t (w-\frac{w}{2})\times cnt (w−2w)×cnt 大的放前面,这个才是操作一次消除的总值,不能把 w × c n t w\times cnt w×cnt 放前面,因为除法下取整会出问题
#include <bits/stdc++.h>
using namespace std;
#define int long long
using i64 = long long;
typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;
const int N = 1e6 + 10;
const int maxn = 1e6 + 10;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;
struct node {
int w, cnt;
bool operator< (const node &tmp) const {
return (w - w / 2) * cnt < (tmp.w - tmp.w / 2) * tmp.cnt;
}
};
void solve()
{
int n, S;
cin >> n >> S;
vector<vector<PII>> g(n + 1);
for (int i = 0; i < n - 1; i ++ )
{
int u, v, w;
cin >> u >> v >> w;
g[u].push_back({v, w});
g[v].push_back({u, w});
}
vector<PII> dp(n + 1); // 当前点的父边要用多少次
function<void(int, int)> dfs = [&](int u, int fa)
{
if (g[u].size() == 1 && u != 1)
{
dp[u].first ++ ;
return;
}
for (int i = 0; i < g[u].size(); i ++ )
{
int ver = g[u][i].first, w = g[u][i].second;
if (ver == fa) continue;
dp[ver].second = w;
dfs(ver, u);
dp[u].first += dp[ver].first;
}
};
dfs(1, -1);
priority_queue<node> q;
int tot = 0, ans = 0;
for (int i = 2; i <= n; i ++ )
{
tot += dp[i].first * dp[i].second;
q.push({dp[i].second, dp[i].first});
}
while (tot > S)
{
auto t = q.top();
q.pop();
tot = tot - t.w * t.cnt + t.w / 2 * t.cnt;
q.push({t.w / 2, t.cnt});
ans ++ ;
}
cout << ans << '\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
}
1294F - Three Paths on a Tree(树的直径 *2000)
- 这一题的思路也基本正确,即直径的两个端点一定选,其余点中选一个最优的
- 但是实现上出了大问题,自己实现的时候很麻烦,还去找两个端点的lca防止重复计算,实际上重复计算也没什么,最后的结果直接除以2就好了
#include <bits/stdc++.h>
using namespace std;
#define int long long
using i64 = long long;
typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;
const int N = 1e6 + 10;
const int maxn = 1e6 + 10;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;
void solve()
{
int n;
cin >> n;
vector<vector<int>> g(n + 1);
for (int i = 1; i <= n - 1; i ++ )
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
int c = 1, d, e;
vector<int> dis_c(n + 1), dis_d(n + 1);
function<void(int, int, vector<int>&)> dfs = [&](int u, int fa, vector<int>&dis)
{
for (int v : g[u])
{
if (v == fa) continue;
dis[v] = dis[u] + 1; // 如边有权值,把1换成权值即可
if (dis[v] > dis[c]) c = v; // 更新最大距离的点
dfs(v, u, dis);
}
};
dfs(1, -1, dis_d);
d = c;
dis_d[c] = 0;
dfs(c, -1, dis_d);
dis_c[c] = 0;
d = c;
dfs(c, -1, dis_c);
int ans = 0;
for (int i = 1; i <= n; i ++ )
{
if (i == c || i == d) continue;
if (dis_c[i] + dis_d[i] + dis_c[c] > ans)
{
e = i;
ans = dis_c[i] + dis_d[i] + dis_c[c];
}
}
cout << ans / 2 << '\n';
cout << c << ' ' << d << ' ' << e << '\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
// cin >> t;
while (t--)
{
solve();
}
}
算法
最大流判定的例题