AGC/ARC
AGC043
A 范围很小, 直接$O(n^4)dp$
B
题意 给定长$n$的序列$a$, 只含$1,2,3$. 有$f_{k,x}=|f_{k-1,x}-f_{k-1,x+1}|,f_{1,x}=a_x$. 求$f_{n,1}$
- 先特判掉$n=1$的情况, 那么答案只能为$0,1,2$
- 答案的奇偶性与$\sum a_i \binom{n-1}{i-1}$相同
- $\binom{n}{m}$为奇就等价于$n|m=n$, 可以用$Lucas$定理证明
- 判断是$0$还是$2$, 可以全除以$2$后用相同方法判断
C
题意 给定$3$个图$X,Y,Z$, 节点数均为$n$. 构造一个$n^3$节点的图$W$, 每个点记做$(x,y,z)$
AGC044
A 考虑从$N$转移到$0$的最少花费, 可以发现每次增加时至多增加到$2,3,5$的倍数, 然后记忆化搜索即可
B 每走一个人就更新最短路, 复杂度是O(N^3)
C 从低位到高位建三进制trie, $S$操作相当于全局交换子树, $R$操作将$0$换为$1$,$1$换为$2$,$2$换为$0$继续往高位处理
D
题意 交互题, 要猜一个字符串$S$, 每次可以询问一个串$T$, 返回$S$和$T$的最短编辑距离
- 核心观察是$T$是$S$的子序列等价于最短编辑距离为$|S|-|T|$
- 先对每个字符询问,求出每种字符个数,然后按照哈弗曼树合并每个串即可
E
F
NOMURA Programming Competition 2020
AB 签到
C 核心观察是当前层非叶节点数不超过更深的叶子数的和
D
- 转化为求总连通块数. $-1$所在连通块一定是树, 其余连通块一定是环套树
- 假设有$cnt$个环套树, $m$个树, 显然环套树贡献不会变, 贡献为$cnt (n-1)^m$
- 对于树的贡献, 假设选$k$棵树连成连通块
- $k=1$时, 贡献为$(n-1)^{m-1}\sum ({sz}_i-1) $
- $k>1$时, 贡献为$(k-1)!(n-1)^{m-k}\sum\prod {sz}_i$
E
题意 给定串$T$, 串$S$初始为空, 每次操作在$S$中添加$0$或$1$, 贡献加上$S$奇数位的$1$, 求$S$等于$T$时的最大贡献
考虑逆序操作, 先删$0$一定最优. 考虑从前往后删$0$, 如果$0$后面的一段连续$1$为奇数, 就通过留一个$0$或者不留$0$, 把连续$1$调整到奇数位. 如果后面连续$1$为偶数, 那么$0$全删. 最后再从后往前删掉所有$0$即可
F
一个序列$a$可以排序, 就等价于对于每对$i,j(i<j)$, 若$a_i$和$a_j$异或和不为$2$的幂, 那么$a_i<a_j$
那么一个序列$a$合法, 就等价于对于每对$i,j(i<j)$, 从高位到低位, 第一次出现$a_i$为$1$且$a_j$为$0$后, $a_i$和$a_j$的低位都相等
从高位到低位考虑, 假设存在$i,j(i<j)$, 满足$a_i$为$1$且$a_j$为$0$, 那么$[i,j]$区间内每一个数低位都应该相等, 所以$[i,j]$区间就可以缩为一个数, 枚举区间$[i,j]$的长度, 就有方程
$f_{N,M}=(M+1)f_{N-1,M}+\sum\limits_{x=2}^M(M-x+1)2^{x-2}f_{N-1,M-x+1}$
前缀优化就可以$O(NM)$
AGC045
A $0$的每个后缀线性基都能异或出对应后缀的所有$1$, 那么答案为$0$, 否则为$1$
B
- $0$看成$-1$,转化成求最大子段和的最小值.
- 假设前缀和最小值为$M$时前缀和最大值为$f(M)$, $Z$为$M$的最大值
- 答案就为$min\{f(M)-M :M\le Z\}$
- $M$增大$2$时,$f(M)$至多增大$2$, 最小值一定为$f(Z)-Z$或$f(Z-1)-Z+1$
C
- 假设$a<b$, 合法串等价于把所有长度$\ge a$的$0$替换为$1$后存在长度$\ge b$的$1$
- 考虑不合法方案, 一定是由$<a$的$0$和$<b$的$1$连接起来, 并且每段$1$中可以任意替换$\ge a$的$0$
- 预处理每种长度的$1$替换$\ge a$的$0$的方案, 然后$O(N^2)DP$
D
Tokio Marine & Nichido Fire Insurance Programming Contest 2020
E
F
ARC 104
C
合法状态可以分成若干区间, 每段区间为$(l,l+k),(l+1,l+k+1),...,(l+k-1,l+2k-1)$
可以$dp$判断
#include <bits/stdc++.h> using namespace std; const int N = 210; int n, a[N], b[N], vis[N], dp[N]; int main() { scanf("%d", &n); for (int i=1; i<=n; ++i) scanf("%d%d", a+i, b+i); dp[0] = 1; for (int i=1; i<=n; ++i) { if (a[i]>0) { if (vis[a[i]]) dp[0] = 0; vis[a[i]] = i; } if (b[i]>0) { if (vis[b[i]]) dp[0] = 0; vis[b[i]] = i; } if (a[i]>0&&b[i]>0&&a[i]>=b[i]) dp[0] = 0; } for (int i=1; i<=2*n; ++i) if (dp[i-1]) { for (int j=i+1; j<=2*n; j+=2) { int len = (j-i+1)/2, ok = 1; for (int k=i; k<i+len; ++k) if (vis[k]) { int x = vis[k]; if (a[x]>0&&a[x]!=k||b[x]>0&&b[x]!=k+len) ok = 0; if (vis[k+len]&&vis[k]!=vis[k+len]) ok = 0; } for (int k=i+len; k<i+2*len; ++k) if (vis[k]) { int x = vis[k]; if (a[x]>0&&a[x]!=k-len||b[x]>0&&b[x]!=k) ok = 0; } if (ok) dp[j] = 1; } } puts(dp[2*n]?"Yes":"No"); }
D
题意 给定$n,k$, 对于$1\le x\le n$, 求出从$k$重集$\{1,2,...,n\}$中取出平均值为$x$的方案数
平均数为$x$可以转化为$<x$的数到$x$的距离和等于$>x$的数到$x$的距离和
就转化为从集合$\{1,2,...,x-1\}$与$\{1,2,...,n-x\}$中选出和相同的方案
设${dp}_{i,j}$表示前$i$个数, 每种数最多取$k$个, 和为$j$的方案
${dp}_{i,j}={dp}_{i-1,j}+{dp}_{i-1,j-i}+...+{dp}_{i-1,j-k*i}$
可以发现转移时模$i$相同, 前缀优化一下, 复杂度$O(n^3 k)$
#include <bits/stdc++.h> using namespace std; int dp[105][495005]; int main() { int n, k, m; scanf("%d%d%d", &n, &k, &m); dp[0][0] = 1; int mx = (n-1)*n/2*k; for (int i=1; i<n; ++i) { for (int j=0; j<=mx; ++j) { dp[i][j] = dp[i-1][j]; if (j>=i) dp[i][j] = (dp[i][j]+dp[i][j-i])%m; } for (int j=mx; j>=0; --j) { if (j>=(k+1)*i) dp[i][j] = (dp[i][j]-dp[i][j-(k+1)*i])%m; } } for (int i=1; i<=n; ++i) { int ans = 0; for (int j=0; j<=mx; ++j) { ans = (ans+(long long)dp[i-1][j]*dp[n-i][j])%m; } ans = (ans*(k+1ll)-1)%m; if (ans<0) ans += m; printf("%d\n", ans); } }
E
题意 给定序列$A$, 序列$X_i$在$[1,A_i]$中随机取, 求$X$的期望$LIS$长度
$O(n!)$枚举排列$p$表示每个数大小关系$a_{p1}<a_{p2}<...<a_{pn}$
但是可能存在有数相等的情况, 解决方法是当$p_i<p_{i+1}$时取$<$, $p_i>p_{i+1}$时取$\le$
显然这样可以不重不漏, 并且$LIS$的长度不变
然后$dp$即可, 总复杂度$O(n! n^3)$
#include <bits/stdc++.h> using namespace std; const int N = 10, P = 1e9+7; int n, a[N], b[N], p[N], dp[N][N], f[N], inv[N]; int qpow(long long a, int n) { long long ans = 1; for (; n; a=a*a%P,n>>=1) if (n&1) ans=ans*a%P; return ans; } int main() { scanf("%d", &n); for (int i=1; i<=n; ++i) scanf("%d", a+i), b[i] = a[i]; sort(b+1, b+n+1); for (int i=1; i<=n; ++i) p[i] = i, inv[i] = qpow(i, P-2); int ans = 0; do { memset(dp,0,sizeof dp); dp[0][0] = 1; //dp[i][j] = 把[1,b[i]]填到X[p1],X[p2],...,X[pj] //满足若p[k]<p[k+1]则X[pk]<X[p(k+1)], 若p[k]>p[k+1]则X[pk]<=X[p(k+1)]的方案 for (int i=1; i<=n; ++i) { int L = b[i-1], R = b[i]; memcpy(dp[i],dp[i-1],sizeof dp[0]); for (int j=1; j<=n; ++j) { int r = dp[i-1][j-1]; if (!r) continue; int cnt = 0; //cnt记录<=的个数, dp[i][k] += dp[i-1][j-1]*C(R-L+cnt,k-j+1) for (int k=j; k<=n; ++k) { if (a[p[k]]<R) break; if (k>j&&p[k]<p[k-1]) { ++cnt; r = (long long)r*(R-L+cnt)%P*qpow(R-L+cnt-k+j,P-2)%P; } r = (long long)r*(R-L+cnt-k+j)%P*inv[k-j+1]%P; if (r==0) break; dp[i][k] = (dp[i][k]+r)%P; } } } memset(f,0,sizeof f); int len = 0; for (int i=1; i<=n; ++i) { for (int j=1; j<i; ++j) if (p[i]>p[j]) f[i] = max(f[i], f[j]); len = max(len, ++f[i]); } ans = (ans+(long long)len*dp[n][n])%P; } while (next_permutation(p+1, p+n+1)); for (int i=1; i<=n; ++i) ans = (long long)ans*qpow(a[i], P-2)%P; printf("%d\n", ans); }