1. 3721 Smuggling Marbles
大意: 给定$n+1$节点树, $0$为根节点, 初始在一些节点放一个石子, 然后按顺序进行如下操作.
- 若$0$节点有石子, 则移入盒子
- 所有石子移向父亲节点
- 把所有不少于$2$个石子的节点的石子丢掉
- 若树上还有石子,则返回第一步
对于所有$2^{n+1}$种初始放石子的方案, 求出最后盒子中石子总数之和.
长链剖分, 这道以后再写
2. 3727 Prefix-free Game
两个串$s,t$合法要满足 $s$不为$t$的前缀且$t$不为$s$的前缀.一个字符串集合合法要求满足 每个串长度范围$[1,L]$, 每个串只由$01$组成, 任意两串合法.给定合法字符串集$S$, 两人轮流操作, 每次添加一个字符串, 要求添加后$S$仍然合法, 不能操作则输. 求最后胜负情况.
假设初始$S$为空的情况. 那么初始状态可以看做两棵深度为$L$的满二叉树(因为不能取空串).
每步操作相当于选一个节点$x$, 然后删去$x$的子树以及$x$到根的链. 可以发现删完一定分裂成多颗满二叉树, 所以这样状态就只与二叉树的深度有关, 可以得到
$$SG_{x}=mex\{0,SG_{x-1},SG_{x-1}\oplus SG_{x-2},...,SG_{x-1}\oplus ...\oplus SG_{1}\}$$
打表可以发现$SG_{x}=lowbit(x)$.
所以对于给定初始字符串集合$S$的情况, 用$trie$模拟求出初始$SG$值即可.
#include
#include
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
typedef long long ll;
const int N = 1e6+10;
int n, T, tot;
ll L, ans;
struct {int ch[2];} tr[N<<2];
char s[N];
void add(int &o, char *s) {
if (!o) o = ++tot;
if (*s) add(tr[o].ch[*s=='1'],s+1);
}
void dfs(int o, ll d) {
if (!o) ans ^= d&-d;
else dfs(tr[o].ch[0],d-1),dfs(tr[o].ch[1],d-1);
}
int main() {
scanf("%d%lld", &n, &L);
REP(i,1,n) scanf("%s", s),add(T,s);
dfs(T,L+1);
puts(ans?"Alice":"Bob");
}
View Code
3. 3939 Strange Nim
大意: $n$堆石子, 第$i$堆初始$A_i$, 有一个系数$K_i$, 每次操作假设第$i$堆有$X$个石子, 那么可以拿走的石子范围为$[1,\lfloor\frac{X}{K_i}\rfloor]$. 两人轮流操作, 求最后胜负情况.
打表可以发现$x\%k==0$时$, sg(x,k)=\lfloor\frac{x}{k}\rfloor$.
否则$sg(x,k)=sg(x-\lceil\frac{x}{k}\rceil,k)$.
通过同时减去相同的$\lceil\frac{x}{k}\rceil$来优化, 复杂度就为$O(\sqrt{k})$
#include
#include
using namespace std;
int sg(int x, int k) {
if (x%k==0) return x/k;
int t = x/k+1;
return sg(x-(x%k+t-1)/t*t,k);
}
int main() {
int n;
scanf("%d", &n);
int ans = 0;
while (n--) {
int a, k;
scanf("%d%d", &a, &k);
ans ^= sg(a,k);
}
puts(ans?"Takahashi":"Aoki");
}
View Code
4. 2044 Teleporter
大意: $n$个点, 点$i$后继为$a_i$, 每个点都可以到达$1$, 求最少修改多少后继使得每个点恰好走$k$步能到达点$1$.
$a_1$必须为$1$, 否则$1$和$a_1$一定不能满足条件, 然后$dfs$从叶子往上贪心.
#include
#include
#include
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define pb push_back
using namespace std;
const int N = 1e6+10;
int n, k, ans, a[N], f[N];
vector<int> g[N];
void dfs(int x) {
f[x] = 1;
for (int y:g[x]) dfs(y), f[x] = max(f[x], f[y]+1);
if (f[x]==k&&a[x]!=1) ++ans,f[x]=0;
}
int main() {
scanf("%d%d", &n, &k);
REP(i,1,n) scanf("%d", a+i);
ans = a[1]!=1;
a[1] = 1;
REP(i,2,n) g[a[i]].pb(i);
dfs(1);
printf("%d\n", ans);
}
View Code
5. 2000 Leftmost Ball
大意: 给定$n$个颜色的球, 每种$k$个, 任意排列后将每种球第一个颜色染为$0$, 求能得到多少种序列.
设$f_{i,j}$为当前放了$i$个$0$, $j$种颜色的方案数.
从左到右枚举最前面的空位应该放白球还是放彩球, 若放彩球则将剩余彩球直接分配下去.
#include
#include
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
using namespace std;
typedef long long ll;
const int P = 1e9+7;
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
const int N = 2010, M = 4e6+10;
int n, k, dp[N][N];
int fac[M], ifac[M];
int C(int n, int m) {
if (nreturn 0;
return (ll)fac[n]*ifac[m]%P*ifac[n-m]%P;
}
int main() {
fac[0]=1;
REP(i,1,M-1) fac[i]=(ll)fac[i-1]*i%P;
ifac[M-1]=inv(fac[M-1]);
PER(i,0,M-2) ifac[i]=(ll)ifac[i+1]*(i+1)%P;
scanf("%d%d", &n, &k);
if (k==1) return puts("1"),0;
dp[0][0] = 1;
REP(i,0,n) REP(j,0,i) {
dp[i+1][j] = (dp[i+1][j]+dp[i][j])%P;
dp[i][j+1] = (dp[i][j+1]+(ll)C(n*k-j*(k-1)-i-1,k-2)*dp[i][j])%P;
}
int ans = (ll)dp[n][n]*fac[n]%P;
printf("%d\n", ans);
}
View Code
6. 2020 Unbalanced
大意: 若一个串满足长度不少于$2$且超过一半的字符相同, 则称它为不平衡串. 给定串$s$, 要求输出$s$的任意一个不平衡子串.
众数的套路题. 枚举字符$x$作为众数的情况, $x$看做$1$, 其余字符看做$-1$, 那么就等价于找一个和大于零的区间.
#include
#include
#include <string.h>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define x first
#define y second
using namespace std;
typedef pair<int,int> pii;
const int N = 1e6+10;
int n, f[N][30];
pii mi[30];
char s[N];
int main() {
scanf("%s", s+1);
n = strlen(s+1);
REP(i,1,n) {
memcpy(f[i],f[i-1],sizeof f[0]);
REP(j,'a','z') {
if (s[i]==j) ++f[i][j-'a'];
else --f[i][j-'a'];
}
if (i>1) {
REP(j,0,25) if (f[i][j]-mi[j].x>0) {
return printf("%d %d\n",mi[j].y+1,i),0;
}
}
REP(j,0,25) mi[j] = min(mi[j], pii(f[i-1][j],i-1));
}
puts("-1 -1");
}
View Code
看了其他人题解发现有更简便做法, 因为只需要找一个, 所以直接判断是否存在$XYX$或$XX$这种即可.
7. 2021 Children and Candies
大意: $n$个人分$C$块糖. 定义函数$f(x_1,...,x_n)$, 第$i$个人若分$a$块糖, 则高兴度为$x_i^a$, $f$的值为所有人高兴度的乘积. 给定序列$A,B$, 求$\sum\limits_{x_1=A_1}^{B_1}\sum\limits_{x_2=A_2}^{B_2}\cdots\sum\limits_{x_n=A_n}^{B_n}f(x_1,x_2,...,x_n)$
简单dp题, 设$dp_{i,x}$为前$i$个人分$x$块糖的答案, 可以得到$dp_{i,x}=\sum\limits_{A_i\le k\le B_i}\sum\limits_{y\le x}dp_{i-1,y}k^{x-y}$.
然后前缀优化一下.
#include
#include
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
typedef long long ll;
const int P = 1e9+7, INF = 0x3f3f3f3f;
const int N = 410;
int n, c, a[N], b[N], dp[N][N];
int po[N][N], sum[N][N];
int main() {
REP(i,1,N-1) {
po[i][0] = 1;
REP(j,1,N-1) po[i][j] = (ll)po[i][j-1]*i%P;
}
REP(i,0,N-1) {
sum[i][0] = 1;
REP(j,1,N-1) sum[i][j] = (sum[i][j-1]+po[j][i])%P;
}
cin>>n>>c;
REP(i,1,n) cin>>a[i];
REP(i,1,n) cin>>b[i];
dp[0][0] = 1;
REP(i,1,n) REP(x,0,c) REP(y,0,x) {
int ret = sum[x-y][b[i]]-sum[x-y][a[i]-1];
dp[i][x] = (dp[i][x]+(ll)dp[i-1][y]*ret)%P;
}
int ans = dp[n][c];
if (ans<0) ans += P;
printf("%d\n", ans);
}
View Code
8. 2022 Unhappy Hacking
大意: 键盘有三个键$0,1$和退格, 求按$n$次以后得到字符串$s$的方案数
显然答案只与$n$有关, 所以求出能得到的所有串的方案数最后除以$2^{|s|}$即可
#include
#include
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
typedef long long ll;
const int P = 1e9+7;
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
const int N = 5010;
int n, m, dp[N][N];
char s[N];
void add(int &x, ll y) {x = (x+y)%P;}
int main() {
scanf("%d%s", &n, s+1);
m = strlen(s+1);
dp[0][0] = 1;
REP(i,0,n) REP(j,0,i) if (dp[i][j]) {
add(dp[i+1][j+1],2*dp[i][j]);
add(dp[i+1][max(j-1,0)],dp[i][j]);
}
int ans = (ll)dp[n][m]*inv(qpow(2,m))%P;
printf("%d\n", ans);
}
View Code
9. 2070 Card Game for Three
大意: $A,B,C$三个人初始$n,m,k$张牌, 每张牌上是三个人名字. 每个人出牌顺序固定, 每轮出一张牌, 然后牌上写的人接着出. 谁先出完谁赢. 对于所有$3^{n+m+k}$中出牌顺序, 求先手胜利方案数.
显然对于一个长度为$x$的出牌序列, 对应$3^{n+m+k-x}$种方案.
只需要考虑出$n$张$A$,$i$张$B$,$j$张$C$, 且最后一张为$A$的方案数, 有
$$\begin{align} ans &=\sum\limits_{i=0}^m\sum\limits_{j=0}^k 3^{m+k-i-j}\frac{(n+i+j-1)!}{(n-1)!i!j!} \notag \\ &= \frac{3^{m+k}}{(n-1)!}\sum\limits_{i=0}^m\frac{3^{-i}}{i!}\sum\limits_{j=0}^k\frac{3^{-j}}{j!}(n+i+j-1)! \notag \end{align}$$
记$f(i)=\sum\limits_{j=0}^k\frac{3^{-j}}{j!}(n+i+j-1)!$
有
$$\begin{align} f(i+1)-f(i) &= \sum\limits_{j=0}^k\frac{3^{-j}}{j!}(n+i+j-1)!(n+i+j-1) \notag \\ &= f(i)(n+i-1)+\sum\limits_{j=0}^k \frac{3^{-j}j}{j!}(n+i+j-1)! \notag \\ &= f(i)(n+i-1)+3^{-1}(f(i+1)-\frac{3^{-k}}{k!}(n+i+k)!) \notag \end{align}$$
所以
$$f(i+1)=\frac{3}{2}f(i)(n+i)-\frac{3^{-k}}{2}\frac{(n+i+k)!}{k!}$$
然后就可以$O(n)$做了
#include
#include
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
using namespace std;
typedef long long ll;
const int N = 1e6+10, P = 1e9+7, inv2 = (P+1)/2;
int inv(int x){return x<=1?1:inv(P%x)*(P-(ll)P/x)%P;}
int n,m,k,fac[N],ifac[N],po[N],ipo[N];
int main() {
ifac[0]=fac[0]=po[0]=ipo[0]=1;
REP(i,1,N-1) {
fac[i]=fac[i-1]*(ll)i%P;
po[i]=po[i-1]*3ll%P;
}
ifac[N-1]=inv(fac[N-1]),ipo[N-1]=inv(po[N-1]);
PER(i,1,N-2) {
ifac[i]=ifac[i+1]*(i+1ll)%P;
ipo[i]=ipo[i+1]*3ll%P;
}
cin>>n>>m>>k;
int ans = 0, ret = 0;
REP(j,0,k) ret = (ret+(ll)ipo[j]*ifac[j]%P*fac[n+j-1])%P;
REP(i,0,m) {
ans = (ans+(ll)ipo[i]*ifac[i]%P*ret)%P;
ret = 3ll*inv2%P*ret%P*(n+i)%P;
ret = (ret-(ll)ipo[k]*inv2%P*fac[n+i+k]%P*ifac[k])%P;
}
ans = (ll)ans*po[m+k]%P*ifac[n-1]%P;
if (ans<0) ans += P;
printf("%d\n", ans);
}
View Code
10. 4257 Factorization
大意: 给定$n,m$, 求长为$n$的序列,乘积等于$m$的方案数.
记$f_{i,x}$为$i$个数乘积$x$的方案, 就有$f_{i,x}=\sum\limits_{ab=x}f_{i-1,a}f_{1,b}$
所以$f$就为积性函数, 有$f_{n,p^k}=\binom{n+k-1}{k}$, 然后相乘即可.
#include
#include
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
using namespace std;
typedef long long ll;
const int P = 1e9+7, INF = 0x3f3f3f3f;
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
const int N = 1e6+10;
int n, m, fac[N], ifac[N];
int C(int n, int m) {
if (nreturn 0;
return fac[n]*(ll)ifac[m]%P*ifac[n-m]%P;
}
int main() {
fac[0]=1;
REP(i,1,N-1) fac[i]=(ll)fac[i-1]*i%P;
ifac[N-1]=inv(fac[N-1]);
PER(i,0,N-2) ifac[i]=(ll)ifac[i+1]*(i+1)%P;
scanf("%d%d", &n, &m);
int ans = 1;
for (int i=2; i*i<=m; ++i) {
int cnt = 0;
while (m%i==0) m/=i,++cnt;
ans = (ll)ans*C(n+cnt-1,cnt)%P;
}
if (m>1) ans = (ll)ans*n%P;
printf("%d\n", ans);
}
View Code
11. 3606 Combination Lock
大意:给定字符串$S$, $n$种操作$(L,R)$, 将$s[L...R]$字符加$1$, $z$变为$a$. 每种操作可以执行任意次, 可以按任意顺序执行, 求能否变为回文串.
区间加可以差分为$++c[l],--c[r+1]$, 回文限制相当于所有对称位置的差分之和为$0$.
连边看每个连通块的和是否为$0$即可.
#include
#include
#include
#include <string.h>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define pb push_back
using namespace std;
const int N = 1e6+10;
int n, m, sum, c[N], vis[N];
char s[N];
vector<int> g[N];
void add(int x, int y) {
g[x].pb(y),g[y].pb(x);
}
void dfs(int x) {
if (vis[x]) return;
vis[x] = 1;
sum = (sum+c[x])%26;
for (int y:g[x]) dfs(y);
}
int main() {
cin>>s+1;
m = strlen(s+1);
REP(i,1,m+1) {
c[i]=(s[i]-s[i-1])%26;
add(i,m+2-i);
}
cin>>n;
while (n--) {
int l,r;
cin>>l>>r;
add(l,r+1);
}
REP(i,1,m+1) {
sum = 0;
dfs(i);
if (sum) return puts("NO"),0;
}
puts("YES");
}
View Code
12. 3605 Zabuton
大意: $n$个人, 初始高度为$0$, 若当前高度不超过$h_i$, 那么第$i$个人可以叠上去, 使高度增加$p_i$, 求最多能叠多少个人.
贪心按$h+p$排序, 然后$dp$一定最优, 考虑证明.
对于两个人$(h_a,p_a),(h_b,p_b)$, 假设$a,b$之前的和为$x$.
若$a$排在$b$前, 有$x\le min(h_a,h_b-p_a)$, 否则有$x\le min(h_b,h_a-p_b)$.
$a$在前比$b$在前更优等价于$min(h_a,h_b-p_a)>min(h_b,h_a-p_b)$
去掉$min$可以得到$h_a+p_a
#include
#include
#include
#include
#include
#include <set>
#include
View Code
13. 2292 Division into Two
大意: 给定序列, 求划分为两个集合$X,Y$, 满足$X$中任意两数之差的绝对值不少于$A$, $Y$中任意两数之差的绝对值不少于$B$, 求方案数.
$DP$好题.
首先$O(n^2)$的$DP$很容易想, 只要枚举上次出现位置即可.
#include
#include
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
typedef long long ll;
const int P = 1e9+7, INF = 0x3f3f3f3f;
const int N = 1e3+10;
int n, dp[2][2][N];
ll a,b,s[N];
void add(int &x, int y) {x+=y;if (x>=P)x-=P;}
int main() {
cin>>n>>a>>b;
REP(i,1,n) cin>>s[i];
s[0] = -1e18;
int cur = 0, ans = 0;
dp[0][0][0] = 1;
//dp[i][z][j]
//z为0, X上个数位置在i, Y上个数位置在j
//z为1, X上个数位置在j, Y上个数位置在i
REP(i,1,n) {
cur ^= 1;
memset(dp[cur],0,sizeof dp[cur]);
REP(j,0,i-1) REP(z,0,1) {
int &r = dp[!cur][z][j];
if (!r) continue;
if (!z&&s[i]-s[i-1]>=a||z&&s[i]-s[i-1]>=b) {
add(dp[cur][z][j],r);
if (i==n) add(ans,r);
}
if (!z&&s[i]-s[j]>=b||z&&s[i]-s[j]>=a) {
add(dp[cur][!z][i-1],r);
if (i==n) add(ans,r);
}
}
}
printf("%d\n", ans);
}
View Code
考虑$O(n)$的做法, 记$dp_i$为集合$Y$取第$i$个数的方案数, 可转移的$j$要满足
$$s_i-s_j\ge B$$
$$s_k-s_{k-1}\ge A,k\in [j+2,i-1]$$
所以$j$是一段连续的区间, 可以前缀和优化一下即可$O(n)$
#include
#include
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
typedef long long ll;
const int P = 1e9+7;
const int N = 1e6+10;
int n,f[N];
ll s[N],a,b;
int main() {
cin>>n>>a>>b;
if (a>b) swap(a,b);
REP(i,1,n) cin>>s[i];
REP(i,3,n) if (s[i]-s[i-2]return puts("0"),0;
f[0] = 1;
int l=0, r=0;
s[n+1] = 2e18;
REP(i,1,n+1) {
while (r1&&s[i]-s[r+1]>=b) ++r;
if (l<=r) f[i]=(f[r]-(l?f[l-1]:0))%P;
f[i] = (f[i]+f[i-1])%P;
if (i>1&&s[i]-s[i-1]1;
}
int ans = (f[n+1]-f[n])%P;
if (ans<0) ans+=P;
printf("%d\n",ans);
}
View Code
14. 3673 NRE
大意: 序列$a$初始全$0$, 给定01序列$b$, $q$个操作$(l,r)$, 表示把$a_l,...,a_r$全改为$1$. 求选择一些操作, 使得最后$a,b$的汉明距离最小
刚开始想着枚举位置$dp$, $wa$了一发才发现转移是不对的. 正解是把所有区间按左端点排序, 记$dp_i$为只考虑前$i$个区间的答案, 那么有
$$dp_i = \begin{cases} dp_j+s_0[r_i]-s_0[r_j], & l_j\le l_i\le r_j\le r_i \\ dp_j+s_0[r_i]-s_0[l_i-1]+s_1[l_i-1]-s_1[r_j], & r_j
#include
#include
#include
#include
#include
#include <set>
#include
View Code
15. 3733 Papple Sort
大意: 给定字符串, 每次操作交换相邻字符, 求变成回文所需要最少操作数.
考虑每种字符的出现位置, 同种字符间显然不会产生交换, 那么每种字符相对位置是不变的, 假设出现位置为$p_1,p_2,...,p_r$, 那么最终$p_1$与$p_r$配对, $p_2$与$p_{r-1}$配对, 以此类推. 记$p_1,p_2,...,p_{r/2}$为$A$, 其余为$B$. 也就是说最优情况$A$全在左侧, $B$全在右侧. 可以发现对于每种局面, 固定$A$不动, 依次把$B$贪心放到$A$的对称位置一定最优. 所以贪心从左到右枚举, 每次遍历到$A$时, $A$的位置一定是在左半边(因为这个$A$是当前未匹配的位置最小的$A$), 把对应的$B$移到对应位置即可. 最后需要注意特判长度为奇数的情况, 需要把中间的字符留到最后再移动. 具体实现用树状数组模拟即可.
#include
#include
#include
#include
#include
#include <set>
#include
View Code
16. 1982 Arrays and Palindrome
大意: 有两个序列$a,b$, 保证$sum(a)=N,sum(b)=N$. 满足对于任意序列: 若前$a_1$个字符,接着$a_2$个字符,...都是回文串. 前$b_1$个字符, 接着前$b_2$个字符,...都是回文串. 那么这个序列所有字符相等. 现在给出$a$的一个排列$A$, 求恢复序列$a,b$.
奇数个数$>2$不成立, 否则把奇数放两侧, 让$b[1]=a[1]+1$,$b[m]=a[m]-1$,其余$b[i]=a[i]$即可.要注意特判$m=1$的情况, 和两个奇数都为$1$的情况
#include
#include
#include
#include
#include
#include <set>
#include
View Code
17. 1983 BBQ Hard
大意: $n$个套餐, 第$i$个套餐有一根钎子, $a_i$个牛肉, $b_i$个青椒. 钎子有标号, 牛肉青椒无标号. 求选出两个套餐组成串成烤肉串的方案. 具体可以看样例图解.
容易得到答案为$\sum\limits_{1\le i转化为求$\sum\limits_{i=1}^n\sum\limits_{j=1}^n \frac{(a_i+a_j+b_i+b_j)!}{(a_i+a_j)!(b_i+b_j)!}$
可以发现答案为所有$(-a_i,-b_i)$走到$(a_j,b_j)$的格路径数, $S$连向每个点$(-a,-b)$, $(a,b)$连向$T$, $dp$求出$S$到$T$的路径条数即可
#include
#include
#include
#include
#include
#include <set>
#include
View Code
18. 1984 Wide Swap
大意: 给定一个排列$p$, 每次操作选择$i,j$, 要求$j-i\ge k, abs(p_i-p_j)=1$, 然后交换$p_i,p_j$. 可以进行人一次操作, 求使排列$p$字典序最小方案.
agc的题都好有意思. 核心观察是可以用$pos_i$表示$i$在$p$中的位置, 那么就转化为每次交换相邻数, 要求最后$1$尽量在最前, 然后$2$尽量在最前, 以此类推. 这样就转化为[HNOI2015]菜肴制作, 建图拓扑即可. 不过这个题似乎直接用小根堆拓扑就能得到正确答案, 可能是因为这题建出的图的性质比较特殊.
#include
#include
#include
#include
#include
#include <set>
#include
View Code
19. 1999 Candy Piles
大意: $n$堆石子, 两人轮流操作, 每次有两种选择: (1)把石子最多的堆的石子全拿走 (2)每个非空堆拿一个. 拿走最后一个的人输, 求最后胜负情况.
石子降序排序, 每堆看成一个矩形, 然后转成棋盘博弈问题. 可以发现一个对角线上的点状态相同, 求出初始位置所在对角线与所有矩形边界的交点判断一下即可.
#include
#include
#include
#include
#include
#include <set>
#include
View Code
20. 1998 Stamp Rally
大意: 给定无向连通图, 每次询问两人分别从点$x,y$出发, 一共遍历$z$个点后结束, 求遍历到的边的最大标号的最小值
单询问的话可以直接从小到大添边, 当$x,y$所在连通块大小不少于$z$的时候就为答案. 多组询问套个整体二分即可.
#include
#include
#include
#include
#include
#include <set>
#include
View Code
21. 2040 Best Representation
大意: 若一个字符串$x$满足长度至少为$1$, 对于任意串$y$,任意整数$k\le 2$, $x$不等于$y$重复$k$次, 那么$x$为好字符串. 若一个字符串的划分的每一部分都是好字符串, 那么这个划分为好划分. 给定字符串$w$, 求$w$最小的好划分, 以及最小好划分的个数.
最小好划分大小要么为$n$, 要么为$1$, 要么为$2$. 这样枚举间断点, $kmp$求一下循环节即可.
#include
#include
#include
#include
#include
#include <set>
#include
View Code
22. 2042 Colorful Slimes
大意: $n$种史莱姆, 抓第$i$种花费$a_i$时间, 可以花费$x$时间把已经抓到的史莱姆种类循环右移一下, 求最少时间抓完$n$种史莱姆.
假设循环右移$t$次, 那么相当于第$i$种史莱姆可以取$[i-t+1,i]$的最小值, 所以枚举右移次数即可.
#include
#include
#include
#include
#include
#include <set>
#include
View Code
23. 2045 Salvage Robots
大意: 给定棋盘, 有一个出口, 一些点有机器人, 每次操作选一个方向, 让所有机器人向该方向走一格, 走出边界的机器人立即死亡, 走到出口的得救. 可以操作任意次, 求最多救多少个机器人.
范围很小, 可以直接暴力区间$dp$. 卡空间, 需要滚动一下数组
#include
#include
#include
#include
#include
#include <set>
#include
View Code
24. 3734 Christmas Tree
大意: 要求用$A$条长度不超过$B$的链合并为一颗给定树, 求$(A,B)$字典序最小值
先考虑$A$的最小值, 假设奇数度数的点有$O$个, 那么显然$O$是偶数, 并且$A$的最小值就为$\frac{O}{2}$. 这是因为每次在一棵树上接上一条链, 最多产生两个奇数度数的点. 所以最小值就是$\frac{O}{2}$, 每次都选两个奇数度数点作为路径端点就可以达到$\frac{O}{2}$. 然后求$B$最小值, 问题可以转化为给定$O$个特殊点, 要求两两组合的路径长度最大值的最小值, 可以二分答案然后树形dp求出. $dp$时要选一个奇数度数的点作为根, 保证根节点儿子数为奇数.
#include
#include
#include
#include
#include
#include <set>
#include
View Code
25. 2689 Prime Flip
大意: 给定$01$序列, 每次操作选一个长度为奇素数的区间翻转, 求最少多少次使序列全零.
奇素数区间花费为$1$, 偶数长的区间花费为$2$, 否则花费为$3$
可以先差分一下, 转化为每次选两个相差为奇素数的点改变颜色, 可以先跑出最大匹配, 尽量匹配奇素数区间, 然后匹配偶数区间, 最后若有剩余则花费为3
#include
#include
#include
#include
#include
#include <set>
#include
View Code
26. 2698 Don't Be a Subsequence
大意: 给定一个串$s$, 求最短的串$t$, 使得$t$不是$s$的子串, 多种方案时输出字典序最小的.
对于一个串的答案可以贪心, 每当出现'a'-'z'的所有字母后划分一个区间, 最短长度就为区间数+1. 所以可以预处理出每个后缀的答案, 然后序列自动机贪心即可
#include
#include
#include
#include
#include
#include <set>
#include
View Code
27. 2699 Flip and Rectangles
大意: 给定棋盘, 每次操作翻转一行或一列, 求最大全$1$矩形面积
可以发现一个矩形如果能全变为$1$, 那么等价于任意一个$2\times 2$的矩形$1$的个数为偶数. 所以就转化为简单的最大全$1$矩形问题, 要特判一下长或宽为$1$的矩形
#include
#include
#include
#include
#include
#include <set>
#include
View Code
28. 2363 Tautonym Puzzle
大意: 若一个序列满足前一半与后一半相同, 则为好序列. 求构造一个序列使得非空好子序列个数为$N$
构造题. 一个想法是连续$x$个数贡献是$2^x-1$, 然后直接划分成若干个连续相同的段, 但是这样长度会超限.
一个倍增的做法是, 对于每种数字只用两次, 第一次全在串$A$内, 第二次全在串$B$内, 并且考虑空子序列的情况.
假设$AB$的答案为$x$, 那么新添一个数字$t$, $tAtB$答案为$2x$, $tABt$答案为$x+1$, 直接递归即可.
#include
#include
#include
#include
#include
#include <set>
#include
View Code
29. 2366 Prefix Median
大意: 给定长$2n-1$的序列$a$, 求所有$a$的排列中, 能生成多少种序列$b$. 其中$b_i$为$a$中前$2i-1$个数的中位数.
好难的$dp$题, 参考了好多篇题解才大概懂, 不愧是AGC的F题
先把$a$排序, 可以注意到$b$必须满足三个条件
- $b_{i}$是$\{ a_i,a_{i+1},...,a_{2n-i}\}$中的一个数
- 不存在$i
- 不存在$ib_{i}>b_{j+1}$
下面证明这三个条件是充分的, 也就是说证明所有满足三个条件的序列$b$都可以构造出对应的$a$.
只考虑$a$是排列的情况, 有重复元素的话证明类似
显然$b_n=n, b_{n-1}$为$n-1,n,n+1$其中之一
- 若$b_{n-1}=n-1$, 那么从$\{n,...,2n-1\}$中取出最小的两个未在$b_1,...,b_{n-1}$中出现过的数, 填到$a_{2n-2},a_{2n-1}$中
- 若$b_{n-1}=n+1$, 那么从$\{1,...,n\}$中取出最大的两个未在$b_1,...,b_{n-1}$中出现过的数, 填到$a_{2n-2},a_{2n-1}$中
- 若$b_{n-1}=n$, 那么从$\{1,...,n-1\}$中取最大未出现的数, $\{n+1,...,2n-1\}$中取最小, 填到$a_{2n-2},a_{2n-1}$中
这样取完后, $b_{n-1}$一定为剩余数的中位数, 由于三个条件的限制, 那么$b_{n-2}$一定是剩余数中中间的三个之一, 所以递归下去即可构造出$a$.
然后就是简单$dp$了, 设$dp_{i,j,k}$为已经填完$b_{i+1},...,b_{n}$, 现在要填$b_{i}$, 比$b_{i+1}$小的可选数有$j$个, 比$b_{i+1}$大的可选数有$k$个的方案数. 枚举$b_{i}$的选取情况转移即可, 前缀和优化一下可以达到复杂度$O(n^3)$, 直接暴力转移$O(n^4)$也能过.
#include
#include
#include
#include
#include
#include <set>
#include
O(n^4)
#include
#include
#include
#include
#include
#include <set>
#include
O(n^3)
30. 2703 Shift and Flip
大意: 给定两个01串$A,B$, 每次操作把$A$循环左移或右移, 或者选一个$B_i=1$的$i$, 翻转$A_i$, 求最少操作次数使得$A,B$相同
假设有一个计数器初始为$0$, 左移$-1$,右移$+1$, 最小值为$L$,最大值为$R$,最终值为$d$.
可以发现如果$d$固定, 那么$A$中每个位置的数是否需要翻转就已经固定, 并且可以知道最优情况要么是先左移到$L$,右移到$R$,再移到$d$. 要么是先右移到$R$,左移到$L$,再移到$d$.
然后考虑翻转操作, $B$中位置$i$的$1$可以翻转的区间就为$[i+L,i+R]$, 只要所有的$1$对应区间覆盖到所有$A$中需要翻转的位置, 那么就合法, 翻转的花费就为$A$中需要翻转的位数.
所以可以$O(n^2)$枚举$L,d$, 求出最小合法的右移距离$R$即可.
具体实现的话, 注意到$L$递增的时候, $R$是单调递减的, 可以直接用可撤销链表模拟.
感觉这个题还是比较考验码力的, 花了两个小时才写完, 主要还是我可撤销链表和双指针不熟练, 以后要找时间再打一遍这个题
#include
#include
#include
#include
#include
#include <set>
#include
View Code
31. 2396 Infinite Sequence
大意: 定义一个由$\{1,...,n\}$组成的无限长序列, 满足(1)第$n$项及以后的项全相等(2)第$i$项之后的$a_i$项相等, 求所有不同的序列个数
设${dp}_i$为$i...n$的填数方案, 那么有${dp}_n=n,{dp}_{n-1}=n^2$
对于$i\le n-2$, 若填$1$, 方案数为${dp}_{i+1}$
若连续填两个$>1$的数, 那么之后全部相同, 方案数为$(n-1)^2$
若填一个$>1$的数$x$, 后面接$x$个$1$, 那么总方案数为$\sum\limits_{j=i+3}^n f[j]$, 再加上填的$1$超出$n$的部分$i+1$
#include
#include
#include
#include
#include
#include <set>
#include
View Code
32. 2401 Alice in linear land
大意: 有一条直线, $Alice$初始在$0$, 要去位置$D$. 给定一个序列$d$, 第$i$步若向终点走$d_i$能缩短到终点的距离就走$d_i$, 否则停在原地不动. 给定$Q$个询问$q_i$, 若修改$d_{q_i}$的值后, 能使$Alice$不能到达位置$D$输出YES, 否则输出NO
假设$a_i$表示前$i$次操作后的距终点距离, 那么修改第$q$次操作后, 距离范围就为$[0,a_{q-1}]$
设$b_i$为最小数, 满足前$i$步后若在$b_i$则不能达到终点, 那么显然只要$b_q\le a_{q-1}$答案就为YES, 可以得到
- 显然$b_n=1$
- 若$b_{i+1}\le \lfloor\frac{d_{i+1}}{2}\rfloor, b_{i}=b_{i+1}$
- 否则, $b_{i}=b_{i+1}+d_{i+1}$
#include
#include
#include
#include
#include
#include <set>
#include
View Code
33. 2062 ~K Perm Counting
大意: 给定$n,k$, 求不存在$|p_i-i|=k$的$n$排列个数.
假设有一个二分图, $L$部为序列下标, $R$部为值, 那么排列$p$对应了一个完美匹配.
考虑一个只含边$(L_i,R_{i+k}),(L_i,R_{i-k})$的二分图
假设它大小为$k$的匹配数为$M_k$, 根据容斥得到答案就为$\sum\limits_{i=0}^n (-1)^i M_i (n-i)!$
$M_k$可以很容易用$O(n^2)$的$dp$求出.
#include
#include
#include
#include
#include
#include <set>
#include
O(n^2)
实际上可以发现对于一条长$x$的链, 匹配数为$k$就相当于取$k$条不相邻的边
那么方案数就为$\binom{x-k+1}{k}$, 用$NTT$把所有链贡献乘一下即可, 复杂度$O(nlogn)$
#include
#include
#include
#include
#include
#include <set>
#include
O(nlogn)
34. 4511 Tree Burning
大意: 长$L$的环, 初始在$0$, 有$n$棵树, 每次顺时针或逆时针走, 烧掉第一棵为被烧的树, 求烧完所有树的最大距离和.
这个agc的$B$题竟然不会, 看了题解才懂
可以发现最优解一定形如LLLRLRLRL, 或者RRRLRLRLR
枚举转折点, 可以发现终点贡献是终点到起点的距离, 其余点的贡献是它到起点距离的2倍.
#include
#include
#include
#include
#include
#include <set>
#include
View Code
35. 4512 Coloring Torus
大意: 给定$k$种颜色, 求构造一个$n\times n$的棋盘, 要求每个格子都涂上一种颜色, 每种颜色都至少用一次. 对于同种颜色的格子, 要求相邻格每种颜色的出现次数相同. 相邻指的是循环相邻.
注意到$a_{i,j}=(i+j)\mod n$时一定合法. 若$n$为偶数, 把奇数列全加上$n$仍然合法, 把$n+i$换成$i$仍然合法. 所以就可以构造出$[n,2n]$的所有$k$.
#include
#include
#include
#include
#include
#include <set>
#include
View Code
36. 4513 Inversion Sum
大意: 给定$n$元素序列$a$, $q$个操作$(x,y)$表示交换$a_x,a_y$. 按顺序执行$q$次操作, 可以选择跳过操作. 对于$2^q$种情况, 求出逆序对的和.
$O(n^3)$的$dp$很容易, 只需要设$dp_{i,j,k}$为前$i$次操作$a_j>a_k$的方案数, 暴力转移即可.
实际上每次转移影响到的状态会很少, 只有$O(n)$, 可以把方案数全除以$2$, 转成概率$dp$来做.
#include
#include
#include
#include
#include
#include <set>
#include
O(n^3)
#include
#include
#include
#include
#include
#include <set>
#include
O(n^2)
37. 3971 rng_10s
大意: 初始商店$A$瓶饮料, 一个人每天早上来买$B$瓶, 晚上若商店饮料数不超过$C$那么老板补充$D$瓶饮料, 求这个人是否每天都能买到$B$瓶.
$B
38. 2003 BBuBBBlesort!
大意: 给定序列$a$, 元素各不相同, 每次操作翻转连续两位或三位, 求翻转两位的最少次数, 使序列升序排列.
题目保证没有重复元素, 那就等价于给定一个排列. 翻转三位相当于是交换间隔2的元素, 那么每次交换不改变与最终位置的奇偶性
39. 4169 Colorful Sequences
大意: 若一个序列存在一段$k$的子串为$1...k$的排列, 那么这个序列为合法序列. 给定$n,m,k$,给定长$m$的序列$A$, 求$A$在所有长$n$的合法序列中的出现次数和.
$A$在所有序列中的出现次数为$(n-m+1)k^{n-m}$, 转化为求$A$在不合法序列中的出现次数和.
(1)若$A$已经合法, 那么答案为$0$.
(2)若$A$中元素互不相同, 那么可以$dp$求出长$m$的所有互不相同的序列在所有不合法序列中的方案, 最后再除以$\frac{k!}{(k-m)!}$.
设状态$(i,j)$表示长$i$,最后一段连续互不相同的段长$j$, 那么转移图很容易画出来.
定义所有$m\le j\le k-1$的点的权值为$1$, 否则为$0$, 那么答案就为从$(0,0)$到$(n,1...k-1)$所有路径的点权和.
记$f_{i,j}$为$(0,0)$到$(i,j)$的路径数, $g_{i,j}$为$(0,0)$到$(i,j)$的所有路径的点权和, 转移可以后缀优化在$O(nk)$的时间求出.
(3)若$A$中元素有相同的, 那么存在一个互不相同的前缀和后缀, 两部分是独立的, 并且总的方案只与这两部分有关. 可以枚举$A$在所有不合法序列中的出现位置, $dp$算出对应不合法序列数, $dp$过程和(2)类似.
#include
#include
#include
#include
#include
#include <set>
#include
View Code