目录
C-岗位分配
思路:采用隔板法,对于需求为k的岗位,提前安排k-1个人,计算所有提前插入的总人数sum,在m-sum和m-sum-1中加入隔板,有空闲的情况就是在m-sum-1中放n个隔板,答案为两种情况之和。
代码:
#include<bits/stdc++.h>
using namespace std;
using ll =long long ;
#define int long long
#define INF 0x3f3f3f3f
const int N=1e5+5;
const int maxn=1e5+5;
const int mod=998244353;
ll inv[maxn], fac[maxn]; //分别表示逆元和阶乘
//快速幂
ll quickPow(ll a, ll b) {
ll ans = 1;
while (b) {
if (b & 1)
ans = (ans * a) % mod;
b >>= 1;
a = (a * a) % mod;
}
return ans;
}
void init() {
//求阶乘
fac[0] = 1;
for (int i = 1; i < maxn; i++) {
fac[i] = fac[i - 1] * i % mod;
}
//求逆元
inv[maxn - 1] = quickPow(fac[maxn - 1], mod - 2);
for (int i = maxn - 2; i >= 0; i--) {
inv[i] = inv[i + 1] * (i + 1) % mod;
}
}
ll C(int n, int m) {
if (m > n) {
return 0;
}
if (m == 0)
return 1;
return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
void solve()
{
init();
int n, m;
cin>>n>>m;
int sum = 0;
for (int i = 0; i < n; i++) {
int a;
cin >> a;
sum += a - 1;
}
m -= sum;
ll ans=C(m - 1, n) + C(m - 1, n - 1);ans%=mod;
cout<<ans;
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
// int _;cin>>_;while(_--)
solve();
}
I-马拉松
思路:如果将顶点 y 作为树根,那么可以知道小明不能正常比赛的马拉松的每一对顶点都是从节点 x 的子树内 的任意节点开始,并在节点y的子树且不包括在节点 z(节点 z 是 y 的直子节点,位于从 x 到 y 的最短路 径上)的子树内的任意节点结束。我们要找的顶点对的总数等于 s[x]·(s[y] - s[z]) ,其中 s[i] 表示节点 i 的 子树的大小,t表示的是y的直子节点,且该点可以使用 DFS 来实现。
代码:
#include <iostream>
#include <vector>
using namespace std;
const int N=3e5+10;
vector<int> g[N];
bool st[N];
long long sun[N];
bool check[N];
int n,x,y;
long long dfs(int u)
{
st[u]=1;
sun[u]=1;
if(u==x)check[u]=1;
for(int i:g[u])
{
if(!st[i])
{
sun[u]+=dfs(i);
check[u]|=check[i];
}
}
return sun[u];
}
int main()
{
cin>>n>>x>>y;
int m=n-1;
for(int i=0,u,v;i<m;i++)
{
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(y);
long long find=0;
for(int i:g[y])
{
if(check[i]==1)
{
find=sun[y]-sun[i];
}
}
long long ans=find*sun[x];
cout<<ans<<endl;
}