C. CCA的子树【树形dp】
题意:
给定一棵 n 个点的带点权的有根树,根节点为 1 。
要选出两个节点,满足任意一个不是另一个的祖先节点,最大化以两个节点为根的子树的点权和 。
如果选不出两棵合法的子树,则输出“Error”。
思路:root保存当前这个点的子树的点权和, dp表示当前这个节点及其子树中最大的点权和。 dfs的时候先处理子树, 然后用d1,d2表示当前点的子树中最大、次大的dp值, 那么当前点的dp值就是max(d1 + d2, root[u])
统计d1, d2的时候实时更新ans即可。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 200010, M = 2 * N;
int idx, e[M], ne[M], h[N], w[M], maxdep, n;
ll dp[N], root[N], ans, dep[N];
void add(int a, int b){ e[idx] = b, ne[idx] = h[a], h[a] = idx ++;}
void dfs(int u, int fa, int depth){
dep[u] = depth, root[u] = w[u];
maxdep = max(maxdep, depth);
ll d1 = 0, d2 = 0;
for(int i = h[u]; ~i; i = ne[i]){
int j = e[i];
if(j == fa) continue;
dfs(j, u, depth + 1);
root[u] += root[j];
if(dp[j] >= d1) d2 = d1, d1 = dp[j];
else if(dp[j] > d2) d2 = dp[j];
dp[u] = max(dp[u], d1);
}
dp[u] = max(dp[u], root[u]);
ans = max(ans, d1 + d2);
}
int main(){
cin >> n;
memset(h, -1, sizeof h);
for(int i = 1; i <= n; ++ i) scanf("%d", &w[i]);
for(int i = 1; i <= n - 1; ++ i){
int a, b;
scanf("%d%d", &a,&b);
add(a, b), add(b, a);
}
dfs(1, 1, 1);
if(maxdep == n) cout << "Error";
else cout << ans;
return 0;
} D. CCA的小球【组合数学 容斥原理】
题意:
给定 n 个小球,每个小球有颜色,要将它们摆成一行 。
两个方案不同,当且仅当存在某个位置,两种方案摆在这个位置的小球颜色不同。
一个方案合法, 当且仅当不存在任意两个位置相邻的小球颜色相同,求合法方案数对 10^9+7 取模后的值 。
n <= 10^6,0 < 颜色编号 < 2^31,每种颜色出现次数 <= 2
思路: 因为每种颜色只有2个球, 所以可以采用捆绑法,然后用容斥原理处理:减掉只有1对颜色相同小球相邻的情况; 加上有2对颜色相同小球相邻的情况…… 注意我们一开始算的时候, 认为小球是有区别的, 所以都按A来算, 最后再除k(相同颜色对数)的阶乘即可。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1000010, mod = 1e9 + 7;
int n,cnt,fact[N],k,inv[N];
ll ksm(ll a, ll b){
ll res = 1;
while(b){
if(b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
ll C(int n, int m){
return (ll)fact[n] * inv[n - m] % mod * inv[m] % mod;
}
void init(int n){
fact[0] = 1;
for(int i = 1; i <= n - 1; ++ i) fact[i] = (ll)fact[i - 1] * i % mod;
inv[n - 1] = ksm(fact[n - 1], mod - 2);
for(int i = n - 2; i >= 0; -- i) inv[i] = (ll)inv[i + 1] * (i + 1) % mod;
}
int main(){
init(1e6 + 5);
scanf("%d",&n);
unordered_map<int, int> mp;
for(int i = 1; i <= n; ++ i){
int col;
scanf("%d", &col);
if(mp.count(col)) k ++; //有k个小球重复
else mp[col] = 1;
}
ll ans = fact[n], tmp = 1;
for(int i = 1; i <= k; ++ i){
tmp = tmp * 2 % mod;
if(i & 1) ans -= C(k, i) * fact[n - i] % mod * tmp % mod;
else ans += C(k, i) * fact[n - i] % mod * tmp % mod;
ans = (ans + mod) % mod;
}
ans = ans * ksm(tmp, mod - 2) % mod;
printf("%lld", ans);
return 0;
}
京公网安备 11010502036488号