AtCoder Regular Contest 116
AtCoder Regular Contest 116
逊吔,只会ABC。
A - Odd vs Even
\(T(1\le T\le 2\times 10^5)\) 组测试数据,每次询问一个正整数 \(N(1\le N\le 2\times 10^{18})\) 的奇数因子多还是偶数因子多。
打表可得
算了证一下
设 \(N=4k+r\)
- \(r = 2\) 时,\(N=4k+2=2(2k+1)\)
偶数因子有2 和 \(2(2k+1)\) ,奇数因子有 \(1\) 和 \(2k+1\)
若 \(d|(2k+1)\) 且\(d\) 不是 1 和 \(2k+1\) ,\(d\) 一定为奇数,且同时会贡献 \(2d\) 这一偶数因子
所以一定是Same - \(r = 0\) 时,\(N=4k\)
偶数因子个数至少是奇数因子的两倍 - \(r=1\) 或 \(3\) 时,
偶数因子为 0 个,奇数因子至少 2 个
const int N = 50 + 10, inf = 0x3f3f3f3f;
void init(){
return;
}
void solve(){
int t; scanf("%d", &t);
while(t--){
ll n;
scanf("%lld", &n);
if(n % 4 == 0) puts("Even");
else if(n % 4 == 2) puts("Same");
else puts("Odd");
}
return;
}
int main(){
init(); solve();
return 0;
}
/*
Even
Odd
Same
Odd
*/
B - Products of Min-Max
给出一个包含 \(n\) 个数的序列 \(A\),有 \(2^n-1\) 个\(A\) 的非空子序列 \(B\)
求 \(\sum max(B)\times min(B)\)
从小到大排序
即可 \(O(n)\) 计算答案。
const int N = 2e5 + 10, inf = 0x3f3f3f3f, mod = 998244353;
int n, a[N];
void init(){
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
sort(a + 1, a + n + 1);
return;
}
void solve(){
int ans = 0;
for(int i = n, ret = 0; i >= 1; i--){
ans = (ans + 1ll * a[i] * a[i] % mod) % mod;
ans = (ans + 1ll * a[i] * ret % mod) % mod;
ret = (2ll * ret % mod + a[i]) % mod;
}
printf("%d\n", ans);
return;
}
int main(){
init(); solve();
return 0;
}
C - Multiple Sequences
给出 \(n(1\le n \le 2e5)\) 和 \(m(1\le m \le 2e5)\),询问有多少满足条件的长度为 \(n\) 的序列 \(A\)。
- \(1\le A_i \le M(i = 1,2,...,N)\)
- \(A_{i+1}\) 是 \(A_i\) 的倍数 \((i = 1,2,...,N-1)\)
注意到如果每次都有改变,顶多有 \(19\) 个数。调和级数一下是 \(O(n\log n)\)
先dp方案数。
枚举有\(i\)个不同的数,对答案的贡献为 \(C(n-1, i-1)*\sum_{j = 1}^m dp[i][j]\)
第一个肯定是第一个,不用选。
const int N = 2e5 + 10, K = 25, inf = 0x3f3f3f3f, mod = 998244353;
int n, m, f[K][N], fac[N], ifac[N];
int power(int x, int y){
int ret = 1;
while(y){
if(y & 1) ret = 1ll * ret * x % mod;
x = 1ll * x * x % mod; y >>= 1;
}
return ret;
}
void init(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i++) f[1][i] = 1;
for(int i = 2; i <= 19; i++)
for(int j = 1; j <= m; j++)
for(int k = 2; 1ll * j * k <= m; k++)
f[i][j * k] = (f[i][j * k] + f[i - 1][j]) % mod;
fac[0] = ifac[0] = 1;
for(int i = 1; i <= n; i++) fac[i] = 1ll * fac[i - 1] * i % mod;
ifac[n] = power(fac[n], mod - 2);
for(int i = n - 1; i >= 1; i--) ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
return;
}
int C(int x, int y){
return 1ll * fac[x] * ifac[y] % mod * ifac[x - y] % mod;
}
void solve(){
int ans = 0;
for(int i = 1; i <= min(19, n); i++){
int ret = 0;
for(int j = 1; j <= m; j++)
ret = (ret + f[i][j]) % mod;
ans = (ans + 1ll * ret * C(n - 1, i - 1) % mod) % mod;
}
printf("%d\n", ans);
return;
}
int main(){
init(); solve();
return 0;
}
D - I Wanna Win The Game
给出 \(n(1\le n \le 5000\) 和 \(m(1\le m \le 5000)\),询问有多少满足条件的长度为 \(n\) 的序列 \(A\)。
- \(\sum_{i} A_i=m\)
- \({Xor}(A_i) =0\)
- \(A_i\ge 0\)
显然每一位都有偶数个数选择。
\(f[i]\) 表示 \(n\) 个数和为 \(i\) 的方案数
\(f[i] += C(n, 2*j) * f[(i - 2 *j) /2]\)
是将之前和为 \((i - 2\times j)/2\) 的 n 个数左移一位,并选 \(2\times j\) 个数最后一位为1
const int N = 5000 + 10, mod = 998244353;
int n, m, fac[N], ifac[N], f[N];
int power(int x, int y){
int ret = 1;
while(y){
if(y & 1) ret = 1ll * ret * x % mod;
x = 1ll * x * x % mod; y >>= 1;
}
return ret;
}
void init(){
scanf("%d%d", &n, &m);
fac[0] = ifac[0] = 1;
for(int i = 1; i <= n; i++) fac[i] = 1ll * fac[i - 1] * i % mod;
ifac[n] = power(fac[n], mod - 2);
for(int i = n - 1; i >= 1; i--) ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
return;
}
int C(int x, int y){
return 1ll * fac[x] * ifac[y] % mod * ifac[x - y] % mod;
}
void solve(){
f[0] = 1;
for(int i = 1; i <= m; i++){
if(i % 2) continue;
for(int j = 0; j <= m && i - 2 * j >= 0; j++)
f[i] = (f[i] + 1ll * C(n, 2 * j) * f[(i - 2 * j) / 2] % mod) % mod;
}
printf("%d\n", f[m]);
return;
}
int main(){
init(); solve();
return 0;
}
E - Spread of Information
有 \(n(1\le n \le 2e5)\) 个点,选择 \(k\) 个为初始感染点,每秒沿边传播(扩张),求最快时间。
二分最快时间(距离)
\(f_u\) \(u\)子树内离他最近的感染点的距离
\(g_u\) \(u\)子树内离他最远的非感染点的距离
如果通过根节点中转能帮上 \(g_u\) 那么一定整个子树都已被覆盖
其他情况,如果 \(g_u= mid\) 那 \(u\) 必须成为初始感染点
const int N = 2e5 + 10, inf = 0x3f3f3f3f;
int n, k, mid, ret, f[N], g[N];
int e, hd[N], to[N << 1], nxt[N << 1];
void add(int u, int v){
to[++e] = v; nxt[e] = hd[u]; hd[u] = e;
}
void init(){
scanf("%d%d", &n, &k);
for(int i = 1, u, v; i < n; i++)
scanf("%d%d", &u, &v), add(u, v), add(v, u);
return;
}
void dfs(int u, int fa){
f[u] = inf; g[u] = 0;
for(int i = hd[u]; i; i = nxt[i]){
int v = to[i]; if(v == fa) continue;
dfs(v, u);
f[u] = min(f[u], f[v] + 1);
g[u] = max(g[u], g[v] + 1);
}
if(f[u] + g[u] <= mid) g[u] = -inf;
else if(g[u] == mid) f[u] = 0, g[u] = -inf, ret++;
}
bool check(){
ret = 0;
dfs(1, 0);
if(g[1] >= 0) ret++;
return ret <= k;
}
void solve(){
int l = 0, r = n, ans = n;
while(l <= r){
mid = (l + r) >> 1;
if(check()) r = mid - 1, ans = mid;
else l = mid + 1;
}
printf("%d\n", ans);
}
int main(){
init(); solve();
return 0;
}
F - Deque Game
\(k\) 个序列,A 和 B 在博弈,A 先手,每次选择一个长度大于 1 的序列,丢掉头或者尾。
A 希望终和最大,B 希望终和最小。
考虑只有一个奇数长度的序列怎么做。
- \(len = 3\) 枚举发现答案为 \(min(a_2,max(a_1,a_3))\)
- 归纳法可得一个奇数长度序列答案为 \(min(a_{mid},max(a_{mid-1}, a_{mid+1}))\)
考虑偶数序列。
奇数个偶数序列时,先操作偶数序列,奇数序列最大权值是 \(max(a_{mid},min(a_{mid-1}, a_{mid+1}))\) ,如果先手先操作奇数序列,后手直接在相反侧取,会导致答案为 \(a_{mid}\),不如先操作偶数序列可能更优。
后手在中途跑去操作奇数序列先手也可以跟去。
偶数个偶数序列时,
如果先手先操作一个偶数序列,操作后就有奇数个偶数序列,后手要是先操作偶数序列,奇数序列给的最小权值是 \(min(a_{mid},max(a_{mid-1}, a_{mid+1}))\) ,但如果后手先操作奇数序列,先手就可以搞成异侧取,弄成 \(a_{mid}\) ,不会优于先操作偶数序列。
如果先手先操作奇数序列,奇数序列答案不变,但偶数序列答案也不变,如果后手先去操作偶数序列,先手就凉了。(好像是这样吧,我晕了)
const int N = 2e5 + 10;
int n, q[N];
vector<int> a[N];
int main(){
scanf("%d", &n);
int cnt = 0;
for(int i = 1; i <= n; i++){
int k; scanf("%d", &k);
for(int j = 0, x; j < k; j++)
scanf("%d", &x), a[i].push_back(x);
cnt += (k & 1 ^ 1);
}
ll sum = 0; int len = 0;
for(int i = 1; i <= n; i++){
if(a[i].size() & 1 ^ 1) {
if(a[i].size() == 2){
sum += min(a[i][0], a[i][1]);
q[++len] = - (max(a[i][0], a[i][1]) - min(a[i][0], a[i][1]));
} else {
int mid0 = a[i].size() / 2 - 1, mid1 = a[i].size() / 2 + 1 - 1, ret0, ret1;
if(cnt & 1 ^ 1){
ret0 = min(a[i][mid0], max(a[i][mid0 - 1], a[i][mid0 + 1]));
ret1 = min(a[i][mid1], max(a[i][mid1 - 1], a[i][mid1 + 1]));
} else {
ret0 = max(a[i][mid0], min(a[i][mid0 - 1], a[i][mid0 + 1]));
ret1 = max(a[i][mid1], min(a[i][mid1 - 1], a[i][mid1 + 1]));
}
sum += min(ret0, ret1);
q[++len] = - (max(ret0, ret1) - min(ret0, ret1));
}
}
}
for(int i = 1; i <= n; i++){
if(a[i].size() & 1) {
if(a[i].size() == 1){
sum += a[i][0];
} else {
int mid0 = (a[i].size() + 1) / 2 - 1;
if(cnt & 1 ^ 1)
sum += min(a[i][mid0], max(a[i][mid0 - 1], a[i][mid0 + 1]));
else
sum += max(a[i][mid0], min(a[i][mid0 - 1], a[i][mid0 + 1]));
}
}
}
sort(q + 1, q + len + 1);
for(int i = 1; i <= len; i += 2) sum -= q[i];
printf("%lld\n", sum);
return 0;
}