4月CWOI杂题
tips:为了避免一不留神题目就被邪恶的 o 老师隐藏,题面文件在 cnblogs 上有备份。
upd:后面没有了。太多了。
C0216 【0407 C组】模拟测试
这场比赛四道题代码加起来长度不超过 1500 个字符,赢!(223+399+330+541=1493)
A 【1231 C组】数组计数
定义 \(f_{i,j}\) 表示前 \(i\) 个数,和为 \(j\) 的方案数,前缀和优化转移。时间复杂度 \(\text{O}(nk)\)。
B 【1231 C组】旅行
因为终点是固定的 \(a_i\),考虑把路径转化成找到一个以 1 为起点和终点的环再减去从 \(a_i\) 到 1 的部分。发现这样问题就等价于给你一些点,找到一个最短的联通所有点的边集,这个环会经过其中每条边两次。每次加点的时候一直向上跳到已有的路径上。因为每个点只经过一次,时间复杂度 \(\text{O}(n)\)。
C 【1231 C组】进化
因为数据给了 \(T=2^d\) 的部分分,这使我们联想到如果能在 \(\text{O}(n)\) 的时间内解决 \(T=2^d\) 的情况,就可以做到 \(\text{O}(n\log T)\) 了。
首先 \(b_i=(a_{i-1}+a_{i+1})\bmod 2=a_{i-1}\oplus a_{i+1}\)。一次进化后,\(a_i\) 会对 \(a_{i-1},a_{i+1}\) 产生贡献;两次进化后,\(a_i\) 会对 \(a_{i-2},a_{i-1},a_i,a_{i+1},a_{i+2}\) 产生贡献 \(\ldots\) 也就是说,“\(a_i\) 在 \(T\) 次进化后会对 \(b_j\) 造成贡献的次数”等于“令 \(s_k=\sum\limits_{t=1}^kx_i\),满足 \(i+s_k\in[1,n]\) 的,长度为 \(T\) 且只包含 \(1,-1\) 的,\(i+s_T=j\) 的数列 \(x\) 的个数。
发现这个 \(i+s_k\in[1,n]\) 的限制很麻烦,考虑把 \(a_i\) 变成一个如图所示的环:

即 \(c_0=c_{n+1}=0,c_i=c_{2n+2-i}=a_i\)。发现对 \(c\) 进化和对 \(a\) 进化是一样的,这很显然。
令 \(m=2n+2\),\(c_i\) 真正会对 \(c_{(i+x-(T-x))\bmod m}\) 产生影响当且仅当 \(c_i\times\binom{T}{x}\equiv 1\pmod 2\),即 \(c_i=1,\binom{T}{x}\equiv 1\pmod 2\)。因为我们此时的 \(T=2^d\),由 Lucas 定理 可知只有当 \(x=0,T\) 时才满足上述条件,即 \(c_i\) 只会影响 \(c_{(i+T)\bmod m},c_{(i-T)\bmod m}\)。一次操作时间复杂度 \(\text{O}(n)\),总时间复杂度 \(\text{O}(n\log T)\)。
D 【1231 C组】Y老板的别墅
定义 \(f_{i,j}\) 表示只考虑 \([i,j]\) 间的房子(可以认为在 \(i-1,j+1\) 处各有一栋无限高的房子)的方案数。转移就是枚举当前范围内最高的楼 \(k\),满足 \(r_k=\min(k-i,j-k)\),\(f_{i,j}\leftarrow f_{i,j}+\binom{k-i}{j-i}\times f_{i,k-1}f_{k+1,j}\)。解释一下:乘组合数是因为我们只规定了范围内每栋房子的相对高度。
发现对于一个 \(k\),满足 \(r_k=\min(k-i,j-k)\) 的 \(i,j\) 很少,所以总的转移是 \(\text{O}(n^2)\) 的。
C0217 【0411 C组】模拟测试
A 【0411 C组】游戏
不难发现最优方案是固定的,可以直接从低到高位模拟。
B 【0411 C组】序列操作
类似思路指路:HDU5828 Rikka with Sequence。
容易发现一个数模上一个比它小的数时至少减小一半。记录区间最大值,如果最大值已经小于 k 就停止下传。
upd:2023.05.08 原来这就是势能分析。
C 【0411 C组】逆序对
不会证结论,不想写。想写可以去看 oeis。
D 【0411 C组】小王献歌
东方神秘的【数据删除】的 字符串技巧。
原题指路:CF914F Substrings in a String
体会到了 if->else 分支优化的巨大力量(暴力 15pts \(\rightarrow\) 46pts)。
考虑优化暴力匹配的过程。对文本串 \(s\) 中每种字符开一个 bitset \(B_c\) 表示字符 c 的所有出现位置。对于求文本串 \(t\) 在 \([l,r]\) 中每次出现的出现位置,定义 \(len\) 为 \(t\) 的长度,我们有以下套路:定义 bitset \(X\),\(X\) 中第 \(i\) 位为 1 表示 \([i,i+len-1]\) 为 \(t\)。遍历 \(t\) 的每一位,同时 \(X\leftarrow X\And B_{t_i}\),最后 \(X\) 中为 1 的位置即为所求。本题中我们统计的是 \(X\) 中 \([l,r-len+1]\) 的答案。注意特判 \(len>r-l+1\) 的情况。
C137 【初2020级】0411 D组考试
原比赛指路:C34 【初2020级】0817 C组-测试3
靠,为什么隐藏了。
D 【0817 C组】二叉树
定义 \(g_{0/1,i,j}\) 表示在 i 的子树内 A/B 最大得分不超过 j 的方案数,\(h_i\) 表示在 i 的子树内任意选的方案数(考虑点权和每个人的选择),有转移式
边界条件是叶子上 \(g_{0/1,u,i}=i\times k,h_{u}=k^2\)。其中 c 表示当前由谁操控,l,r 表示左儿子/右儿子。
定义 \(f_{0/1,i,j}\) 表示钦定一条从 1 到 i 的重链,A/B 得分刚好为 j 的方案数。有转移式
叶子处统计答案即可。时间复杂度 \(\text{O}(nk)\)。
C0220 【0414 C组】模拟测试
没挂分,很高兴。
A 【0414 C组】ZYB建围墙
手玩可以发现最优摆放,是一个正六边形从正下方开始外边贴一圈。
B 【0414 C组】ZYB和售货机
原题目转化成连 \(i\rightarrow f_i\),权为 \(D_{f_i}-c_i\) 的边:对于边 \((u,v,w)\),表示在 \(A_u\neq 0\) 时可以用 \(w\) 的单价卖一个 \(v\),求最大收益。
对于不在环上的点,可以直接以最大价格卖出;对于在环上的点,只需要保留两种最优选择:不在环上的/在环上的。假如一个环上有点的最优选择是在环上的(或者环的大小为 1),则整个环上的点都可以以最优选择卖出;反之,需要牺牲一个点以环上价格卖一个。贪心即可。正确性可以自己手玩一下两种情况(策略就是先都卖到只剩一个,保证最优一选最多)。
C 【0414 C组】ZYB玩字符串
暴力怎么做?在 s 中枚举 p,check 就是递归地每次删除 s 中的 p。
考虑用区间 DP 优化暴力 check 的过程。定义 \(f_{i,j}\) 为 1 表示 \(s_{i,i+1\ldots j}\) 在递归地每次删除 s 中的 p 后剩下的是一个 p 的前缀,反之不是。如果 s 同时满足 \(f_{1,n}=1,\left|s\right|\bmod\left|p\right|=0\) 则可行。转移分两种情况:[i,j] 存在一个完美匹配(即刚好能用题目中的方法表示出来)的后缀,设该后缀长度为 \(k\times len\),那么可以从 \(f_{i,j-k\times len},f_{j-k\times len+1,j}\) 转移过来(其中 len 表示枚举的 p 的长度);[i,j] 不存在一个完美匹配的后缀,那么 \(s_j\) 如果满足 \(s_j=p_{j\bmod len}\),可以从 \(f_{i,j-1}\) 转移过来。这样,check 的时间复杂度就是 \(\text{O}(\frac{n^3}{len})\) 的了。
时间还是比较爆炸,怎么优化?可以随便加一点剪枝:\(\left|s\right|\bmod len=0\) 啊,p 中每个字母出现次数必须是 s 中的因数啊,从短到长枚举 p 啊,p 不重复算啊 \(\ldots\) 等等。
C0223 【0418 C组】模拟测试
T1 好妙。
A 【0418 C组】序列
考场上卡容斥了。
算最终答案是 \(x=i\) 的倍数的方案数很简单,每次选 \(i\) 的倍数就行。怎么容斥?可以倒序枚举 \(x=i\),这样当 \(x=2i,3i\ldots\) 时的答案我们就已经算出来了,可以直接减掉。
B 【0418 C组】 图
启发式合并乱搞就行。
C 【0418 C组】金条争夺
不会。
D 【0418 C组】推销员
发现按 \(s_i\) 从小到大排序后 \(x=k\) 的答案就是 \(\min\limits_{i=1}^n(2s_i+\text{val}(i,k))\),其中 \(\text{val}(x,y)\) 表示前 \(x\) 个房子中最小的 \(y\) 个 \(a_i\) 之和。set 维护一下就做到了 \(\text{O}(n^2)\)。
发现对于 \(p<q\),\(\min\limits_{i=1}^n(2s_i+\text{val}(i,p))\) 和 \(\min\limits_{i=1}^n(2s_i+\text{val}(i,q))\) 的最优决策点是不降的,可以使用分治优化计算(因为如果 \(q\) 的最优决策点小于 \(p\),则 \(p\) 挪到 \(q\) 的位置显然不劣)。
C0224 【0420 C组】模拟测试
考得很爆炸。
呃呃,原题大战。
A 【0420 C组】姨
不取模的屎。
题目中的贡献可以看做一个长为 2n 位的 01 串的子集数,所以可以枚举有 r 行 c 列被染黑,计算包含这个子集的方案数,而不用考虑子集的累加贡献是否与原题中的相同。答案就是 \(\sum\limits_{i=0}^n\sum\limits_{j=0}^n\binom{n}{i}\binom{n}{j}\binom{m-(in+jn-ij)}{k-(in+jn-ij)}\),最后除方案数 \(\binom{m}{k}\)。注意到 \(\binom{n}{i},\binom{n}{j}\) 和 \(\binom{m-(in+jn-ij)}{k-(in+jn-ij)},\binom{m}{k}\) 可以分别预处理。
B 【0420 C组】矿石
对相邻两个安全采矿点的容斥是 trivial 的,思考一种每次把前缀和当前点的答案合并的过程。假设对于点 i,i+1,不变(即同时包含 i,i+1)的区间数设为 A,减少(即包含 i 但不包含 i+1)的区间数设为 B,增加(即不包含 i 但包含 i+1)的区间数设为 C,容斥得到的新答案就是原答案加上 \(2^{A+C}-1\) 再减去 \(2^{A}-1\),维护即可。至于为什么只用跟 i 容斥是因为 i+1 与 i 的交叉部分一定严格包含 i+1 与其他点的交叉部分,算了 i 以后就相当于都算过了。
C 【0420 C组】再见括号序列
md,这不纯脑瘫题???考场上怎么连 70 都没写???我是纯智障吗???
为什么自然溢出没被卡啊()
40pts:\(O(n^3)\) 的区间 dp。
70pts:\(O(n^2)\) 枚举区间丢进栈判断。
100pts:70pts 的优化。记录每个起点为 1 的前缀对应的栈的状态,容易发现区间 [l,r] 合法当且仅当前缀 [1,l-1] 和 [1,r] 对应的栈状态相同,hash 即可。单模被卡,可以双模/自然溢出。
C0229 【0426 C组】模拟测试
究极好题。
A 【0425 C组】匹配
发现答案是 n 个 r 之和减去 n/2 个最小的 l+r。
B 【0425 C组】狼人
首先暴力的树上背包你是会的,然后加上一点剪枝就可以过。
复杂度玄学,至今无人会证。
感觉不好,不想写,也没搞懂自己怎么过的。
C 【0425 C组】旅行
倒着 dp,转移 \(f_i=\max\limits_{j=i+1}^{i+t_j}f_j-\lfloor\frac{j-i}{k}\rfloor d+h_i\)。维护 k 棵大小为 n/k 的 BIT,第 x 棵表示 \(i\bmod=x\) 时随着 \(\lfloor\frac{j-i}{k}\rfloor d\) 的变化(就是前 x-1 个是 \(f_j-md\),后面是 \(f_j-(m+1)d\) 的形式),每块的最大值。这样你一次查询中间那些每块大小为 k 的区间就可以 \(\log n\) 了(具体大小你可以加加减减一下,反正大小关系是不变的)。这样只剩下查询区间的前后两块不完整的区间了。
对于查询区间的头部,他就是 i 在的这一块的一个后缀,扫的时候记一下就行;对于尾部,我们开 n/k 棵大小为 k 的 BIT 表示每块的前缀最大值。
有一种可能就是令 \(R_i=i+t_i\),\(R_i\) 所在块编号为 o,i 在 o 中对应位置 i' 在 \(R_i\) 前面。这样看似会出现错误(从 o 这块的开头到 i'-1 这部分你应该少减一个 d),但是你会发现这部分我们可以单独查询一下少减一个 d 的结果,且这个结果一定会比查询 o 开头到 \(R_i\) 中的这部分的错误结果更优(少减一个 d),所以错误的那部分答案不会影响真实答案。
每次 dp 完一块后再一起更新答案即可。
放一下 code:
#include<bits/stdc++.h>
#define int long long
#define mk make_pair
#define fi first
#define se second
using namespace std;
typedef pair<int,int>pii;
const int inf=1e18;
typedef long long ll;
#define T (1<<15)
char buf[T],*p1=buf,*p2=buf;
#define nc() (p1==p2&&(p2=buf+fread(p1=buf,1,T,stdin),p1==p2)?-1:*p1++)
ll read(){
ll x=0;char c;bool s=0;
do c=nc();while(c!=45&&(c<48||c>57));
if(c==45)c=nc(),s=1;
do x=10*x+(c&15),c=nc();while(c>=48&&c<=57);
return s?-x:x;
}
#undef T
int n,m,k,d,h[2000005],t[2000005],f[2000005];
struct BIT1{
vector<int>c;
void build(){
c.clear();c.resize(m+5);
for(int i=0;i<=m+1;i++)c[i]=-inf;
}
void add(int x,int y){
for(;x<=m;x+=x&-x)c[x]=max(c[x],y);
}
int ask(int x){
int res=-inf;
for(;x;x-=x&-x)res=max(res,c[x]);
return res;
}
};
vector<BIT1>Tr1;//all block
struct BIT2{
vector<int>c;
void build(){
c.clear();c.resize(k+5);
for(int i=0;i<=k+1;i++)c[i]=-inf;
}
void add(int x,int y){
for(;x<=k;x+=x&-x)c[x]=max(c[x],y);
}
int ask(int x){
int res=-inf;
for(;x;x-=x&-x)res=max(res,c[x]);
return res;
}
};
vector<BIT2>Tr2;//every block
int id(int ind){
return (ind-1)%k;
}
int bel[2000005],lt[2000005],rt[2000005];
void solve(int l,int r,int o){
int ma=-inf;BIT2 pre;pre.build();
for(int i=r;i>=l;i--){
int res=-inf,L=i+1,R=i+t[i];
if(i==n){
f[i]=h[i];
pre.add(i-lt[o]+1,f[i]);ma=max(ma,f[i]);
continue;
}
if(R<=r){
res=max(res,pre.ask(R-lt[o]+1));
f[i]=res+h[i];
pre.add(i-lt[o]+1,f[i]);ma=max(ma,f[i]);
continue;
}
res=max(res,ma);
int lim=bel[R]-1;
if(lim>o)res=max(res,Tr1[id(i)].ask(lim)+(o+1)*d);
if(id(R)>=id(i)){
res=max(res,Tr2[lim+1].ask(id(R)+1)-(lim-o+1)*d);
res=max(res,Tr2[lim+1].ask(id(i))-(lim-o)*d);
}
else{
res=max(res,Tr2[lim+1].ask(R-lt[lim+1]+1)-(lim-o)*d);
}
f[i]=res+h[i];
pre.add(i-lt[o]+1,f[i]);ma=max(ma,f[i]);
}
}
void update(int l,int r,int o){
BIT2 tmp;tmp.build();
for(int i=l;i<=r;i++){
tmp.add(i-lt[o]+1,f[i]-(o+1)*d);
}
for(int i=l;i<=r;i++){
Tr1[id(i)].add(o,tmp.ask(k));
tmp.add(i-lt[o]+1,f[i]-o*d);
Tr2[o].add(i-lt[o]+1,f[i]);
}
}
signed main(){
n=read(),k=read(),d=read(),m=(n+k-1)/k;
for(int i=1;i<=n;i++)h[i]=read();
for(int i=1;i<n;i++)t[i]=read();
for(int i=1;i<=n;i++)f[i]=-inf;
Tr1.resize(k+5);
for(int i=0;i<=k+1;i++)Tr1[i].build();
Tr2.resize(m+5);
for(int i=0;i<=m+1;i++)Tr2[i].build();
for(int i=1;i<=n;i++)bel[i]=(i-1)/k+1;
for(int i=1;i<=m;i++)lt[i]=(i-1)*k+1,rt[i]=min(n,i*k);
for(int o=m;o>=1;o--){
solve(lt[o],rt[o],o);
update(lt[o],rt[o],o);
}
int ans=0;
for(int i=1;i<=n;i++)ans^=(f[i]+i);
printf("%lld",ans);
return 0;
}
D 【0425 C组】密码
【数据删除】的构造,没什么营养,没写。
C0232 【0428 C组】模拟测试
A 【0428 C组】前k大
枚举 \(f(n)=x\),则 \(a_n=n+c\times f(n)=p2^x+cx\),其中 p 是一个奇数。这就是一个等差数列的形式,二分答案即可。
B 【0425 C组】石头剪刀布
想不到的线性 dp。
我们记当前要尽力保留的为 W,会产生威胁的为 D,会被利用的叫 S。
定义 \(f_{i,0/1}\) 表示考虑前 \(i\) 个人,末尾是否存在没有被消灭的 D,能保留的最多的 W 的数量。
- 如果 \(s_i\) 为 W:
- \(f_{i,0}=f_{i-1,0}+1\);
- \(f_{i,1}=f_{i-1,1}\)(由于末尾存在没有消灭的 D,所以当前这个 W 没有贡献);
- 如果 \(s_i\) 为 D:定义 \(ph\) 表示 \(i\) 之前的最后一个 S 出现的位置,\(pq\) 表示 \(i\) 之前最后一个 ? 出现的位置
- \(f_{i,0}=\max(f_{ph,0},f_{pq-1,0},f_{pq-1,1})\)(这个 D 要消灭掉;-1 是因为 ? 可能会有贡献,影响答案);
- \(f_{i,1}=\max(f_{i-1,0},f_{i-1,1})\);
- 如果 \(s_i\) 为 S:
- \(f_{i,0}=\max(f_{i-1,0},f_{i-1,1})\)。
- \(f_{i,1}\) 没有值(因为在当前就把 D 消灭掉一定比不消灭更优);
- 如果 \(s_i\) 为 ?:
- \(f_{i,0}= \max(f_{i-1,0}+1,f_{i-1,1})\)(同 S,但 ? 可以产生贡献);
- \(f_{i,1}\) 没有值;
\(O(n)\) 的 dp。喜欢的话可以压空间。
C 【0428 C组】吉司机线段树
首先原序列可以写成一个长为 2n 的操作序列,这个你是会的。
删除可以倒着看做加入。我们记 \(i\) 最后一次 1 操作的时间点为 \(lst_i\),\(mx_i\) 表示从 \(i\) 这个时刻开始往后最大的一次 2 操作的值。我们求的就是 \(\sum\limits_{i=1}^n mx_{lst_i}\),需要支持 \(lst_i\) 的单点改和 \(mx_i\) 的区间改(可以线段树上二分找到插入一个 2 操作后会影响后缀最大值的区间)。线段树送走。
不要像这个 SB 一样忘了在二分的时候下传标记调一年。
D 【0428 C组】排列
连暴力都不会。
C0234 【0430 C组】模拟测试
感觉比上一场小清新了不少(不过好像是 4 道原题?)。
A 【0430 C组】ksum
隐藏条件:\(a_i\ge0\)。
sb 题,有加强版 U295987 ksum (hard version)(\(\left|a_i\right|\le10^9\))(数据不保证正确性,出锅请联系我)。
B 【0420 C组】label
感觉有点神奇。
定义 \(f_{i,j}\) 表示 \(i\) 这个点值为 \(j\) 的方案数,前缀和优化可以做到 \(O(nm)\)。
发现 \(f_i\) 的取值是对称的(显然),且中间有很长一段是相同的,所以我们可以只保留左右至多 \((n-1)k\) 个的值(中间的记一个就够了)。
为什么够了?对于深度为 1 的树(就是叶子节点),显然答案都是 1;对于一棵深度为 2 的树,只有左右两边 k 个会有影响;对于一棵深度为 3 的树,因为转移时左右两边 k 个有变化,所以左右两边 2k 个会有影响 \(\ldots\) 以此类推,n 个点深度至多为 n,故至多 \((n-1)k\) 种变化。时间复杂度 \(O(n^2k)\)。
C 【0430 C组】square
定义 \(f_{i,j}\) 表示以 \((i,j)\) 为左上角能扩展出的最大正方形的边长。这个可以 \(O(n^2\log n)\) 或者 \(O(n^2)\) 预处理。
考虑优化暴力二分答案的过程。假设当前二分的答案为 mid,相当于我们要看从 \((x_1,y_1)\) 到 \((x_2-mid+1,y_2-mid+1)\) 中间 f 的最大值是否不小于 mid。二维 rmq 送走,时间复杂度 \(O(n^2\log^2n)\)。略微卡常,请预处理对数表。
D 【0430 C组】最短路
如果你不会做,再看看题面。
因为保证有解,且保证解是两点之间的最短路,k 个点一定在一条路径上,求“直径”即可。

浙公网安备 33010602011771号