CF 口胡笔记 2200Ct辑
只需要口胡CF题就行啦!(
从前天起口胡 CF 按照洛谷通过人数排序的题单
这期我们来口胡 CF2200 Part 1 吧 ~
CF617E XOR and Favorite Number
给定一个长度为 \(n\) 的序列 \(a\),然后再给一个数字 \(k\),再给出 \(m\) 组询问,每组询问给出一个区间,求这个区间里面有多少个子区间的异或值为 \(k\)。
\(1 \le n,m \le 10 ^ 5\),\(0 \le k,a_i \le 10^6\),\(1 \le l_i \le r_i \le n\)。
想了半天还是不会啊,看看样例吧……
等等,\(k\) 不变?!
Easy! 先把询问离线下来按 \(q_r\) 排序。从左到右依次更新每个点 \(r\)。把 \(\oplus[l,r]=k\) 的区间全部加进树状数组里,索引是 \(l\) 。(显然总共最多 \(2n\) 个区间),询问的时候在 \(q_r\) 点询问 \(l\ge q_l\) 的有多少个区间就行了。
看看题解吧,什么,居然是莫队 \(O(n\sqrt n)\) 的? 不优!(
那我写写我的 \(n \log n\) 高妙方法吧。
好了,TLE
了,原来总共最多 \(\frac{n(n-1)}{2}\) 个区间,我傻了。比如 k=0:0 0 0 0 0 0
或者 k=3:1 1 1 2 2 2
就能卡到 \(O(n^2\log n)\),寄。
那我还是莫队吧,寄。顺便附上错代码。
#include<bits/stdc++.h>
using namespace std;
#define ff(i,l,r) for(auto i=(l);(i)<=(r);++i)
#define fi(l,r) ff(i,l,r)
#define lowbit(x) ((x)&(-(x)))
#define ll long long
#define ul unsigned ll
#define ui unsigned int
#define P 998244353
#define N 100005
#define L 1048578
int a[N],c[N],n,kk,m,fir[L],nxt[N],ans[N],cnt,s[N];
struct Req{
int l,r,i;
}req[N];
void add(int x){++cnt;for(;x<=n;x+=lowbit(x))++c[x];}
int que(int x){int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;}
int main(){
scanf("%d %d %d",&n,&m,&kk);
fi(1,n)scanf("%d",&a[i]);
fi(1,m){scanf("%d %d",&req[i].l,&req[i].r);req[i].i=i;}
sort(req+1,req+m+1,[&](Req x,Req y){return x.r<y.r;});
int it=1;
fi(1,n){
s[i]=s[i-1]^a[i];
for(int e=fir[kk^s[i]];e;e=nxt[e])add(e+1);
nxt[i]=fir[s[i]];fir[s[i]]=i;
if(s[i]==kk)add(1);
for(;it<=m&&req[it].r==i;++it)ans[req[it].i]=cnt-que(req[it].l-1);
}
fi(1,m)printf("%d\n",ans[i]);
return 0;
}
CF13C Sequence
给定一个序列,每次操作可以把某个数加上 \(1\) 或减去 \(1\)。要求把序列变成非降数列。最小化操作次数。
\(n \le 5000,|a_i| \le 10^9\)
设 \(f[i]\) 代表 \(a_i\) 不动,让 \(a_{1 \sim i}\) 不升的最小代价。
这种情况下虽然 \(u\) 点不动最优,但 \(u\) 不动是属于 \(u \rightarrow i\) 这个转移方案里的。 \(j \rightarrow i\) 这个转移必须要动 \(u\) 。要把中间的所有值变成 \(a_i\) 。
\(f[i]=\min(f[j\in[1,i)]+\text{cost}(a_{(j \sim i)}\rightarrow a_i))\)
但是这种方法是错误的。
题解方法是记录 \(f[i][j]\) 代表把 \(a_i\) 变成 \(a_j\) 时维护 \(a_{i\sim j}\) 不升的成本。
.
另外,其实题目要求是维护不降,我现在才发现(
拓展 P4597 Sequence【加强版】
给定一个序列,每次操作可以把某个数加上 \(1\) 或减去 \(1\)。要求把序列变成非降数列。最小化操作次数。
\(n \le 5\times 10^5,|a_i| \le 10^9\)
考虑按顺序加入每个点后,该点权值修改为 \(x\) 时的代价 \(y\) (类似于 \(f[i][j]\)):
没错,永远是个下凸包!
所以只用维护凸包就行了。
ll ans;
int a,n;
priority_queue<int>q;
int main(){
scanf("%d",&n);
fi(1,n){
scanf("%d",&a);
q.push(a);
if(a<q.top()){
ans+=q.top()-a;
q.pop();
q.push(a);
}
}
printf("%lld\n",ans);
return 0;
}
CF570D Tree Requests
给定一个以 \(1\) 为根的 \(n\) 个结点的树,每个点上有一个字母(
a
-z
),每个点的深度定义为该节点到 \(1\) 号结点路径上的点数。\(m\) 个询问,每次询问 \(a, b\) 查询以 \(a\) 为根的子树内深度为 \(b\) 的结点上的字母重新排列之后是否能构成回文串。\(n,m \le 5\times 10^5\)
做法一
启发式合并分别统计每个点 abcdefgh...z
的数量。复杂度 \(O(26n\log n)\)
看题解,可以状压。
做法二
题解还有一种做法,很高级,没有 \(\log\) : dfs 作差。记录 \(\text{cnt}[\text{dep}]\) 代表深度为 \(\text{dep}\) 的异或值。进入的时候记录原先的二进制数,出节点的时候再异或上进入时的内容。复杂度 \(O(n+m)\)
做法三
题解还有一种做法,更高级,在线:记录每个节点的 \(\text{dfn}\) ,分配 \(\text{maxdep}\) 个 vector 存储每个深度里面节点的前缀异或和。每次 lower_bound 查询作差即可。
【悬疑题】 CF280C Game on Tree
给出一棵树,每次随机等概率选择一未染黑的点,将它及其子树染黑。问期望多少次操作可以将树全部染黑。
\(n \le 10^5\)
不会做。看题解:
设 \(f_i\) 代表 \(i\) 被选中次数的期望(\(f_i\in[0,1]\)),也就是被选中的概率。
则 \(\text{ans}=\sum f_i\)
考虑任意一个不一定合法的操作序列,\(i\) 号点未被删除的概率是 \(\frac{i}{\text{dep}[i]}\)
那么 \(f_i\) 就是 \(\frac{1}{\text{dep}[i]}\)
读完题解,问题来了:这个操作序列没有保证合法,为什么能推出 \(f_i\)?
现在我又获得了一种新的做法:
记任意一种合法的操作序列为 \(L\),记所有 \(L\) 的集合为 \(\{L\cdots\}\), 记一个点 \(x\) 的祖先(及自己)的点集为 \(F_x\) ,那么:
可重集 \(A_x=\{L\cap F_x\cdots\}\) 代表的是一条链的操作序列可重集合。
集合 \(C_x=\{L\cap F_x\cdots\}\) 代表的是一条链的操作序列集合。
我猜想, \(\forall c \in C_x , A_x \rightarrow \text{count}(c)\) 相等。
也就说:每一种"链上操作序列"在 \(C_x\) 中出现的次数是一样的。
证明:不会
所以
所以 \(ans=\sum \frac{1}{\text{dep}_i}\)
记录一下隔壁巨佬看到这道题的思路:先考虑链和菊花图的情况
这题存疑,还不太会。
CF559C Gerald and Giant Chess
给定一个 \(h\times w\) 大小的棋盘,其中有 \(n\) 个点为黑色。
每次只能向右或向下移动,求从 \((1,1)\) 不经过黑色点到达 \((h,w)\) 的方案数。\(h,w \le 10^5 ; n\le 2000\)
考虑容斥。
记 \(f[i][0/1]\) 代表最后一个经过的黑点是 \(i\) 号点,迄今经过的黑点数量奇偶为 \(0/1\)
\(f[i][x]=\sum f[j][!x]\times \text{pathcount}(i,j)\)
记 \(U[i][j]=\text{pathcount}(i,j)\) ……
好像不行。看看题解吧……
记 \(f[i]\) 代表从 \(1\) 开始走不经过其他黑点,到达 \(i\) 点的路径数。
\(f[i]=C_{x_i+y_i-2}^{x_i-1}-\sum f[j]\times C_{x_i+y_i-x_j-y_j}^{x_i-x_j}\)
由于每个 \(f_j\) 都有一个独立代表性的必经黑点 \(j\) ,因此这样可以不重不漏地统计答案。
妙啊
CF833B The Bakery
将一个长度为 \(n\) 的序列分为 \(k\) 个连续段,使得总价值最大。
一段区间的价值表示为区间内不同数字的个数。
\(n\leq 35000,k\leq 50\)
看起来是 \(O(nk^{(2?)}(\log?))\) 的做法
记 \(f_{i,j}\) 代表 \(i\) 位置是第 \(j\) 段的最后一个点时,之前的最大总价值。
\(f_{i,j}=\max(f_{k,j-1}+j-k-重复的数量(k+1,i))\)
复杂度 \(O(n^2m)\)
然后就不会了
看看题解吧:
\(不同颜色的数量(i,j)\) 居然可以用线段树求出?!!
如题解图,上面一行数字代表的是 \(a\) ,每个 \(a_i\) 对 \((pre[i]+1) \sim i\) 造成贡献(如图不同颜色区间),这样就可以线段树维护了。
本质是对于一根扫描线,枚举这个线往后第一次出现 x 值是什么时候。那显然,扫描线在左端点的时候最优。
所以我们记录一个点 \((x,y)\) 代表 \(i\) 点造成贡献的区间 \([x,y]\) ,所有 \(x \le k\) 的和 \(y \ge i\) 的区间贡献都被减掉,这是一个典中典二位偏序题目。
对于 \(x\) 的限制,我们可以让线段树节点代表区间的左端点作为 \(x\) 限制。
对于 \(y\) 的限制,在枚举 \(i:1 \rightarrow n\) 时,先不加入线段树上 \(i\) 点以后的区间。
拓展题 CF868F Yet Another Minimization Problem
将一个长度为 \(n\) 的序列分为 \(k\) 个连续段,使得总价值最小。
一段区间的价值表示为区间内相同数字的个数。
\(n\leq 10^5,k\leq 20\)
记 \(f_{i,j}\) 代表 \(i\) 位置是第 \(j\) 段的最后一个点时,之前的最小总价值。
\(f_{i,j}=\min(f_{k,j-1}+j-k-不同颜色的数量(k+1,i))\)
一模一样的题,就变成紫了。
CF1175E Minimal Segment Cover
给定 \(n\) 个线段,每个线段形如 \([l,r]\)。
\(m\) 次询问,每次询问给出 \(x,y\),求至少选多少个线段才能使这些线段的并能包含区间 \([x,y]\)。
\(1\le n,m\le 2\times 10^5\),\(0\le l<r\le 5\times 10^5\),\(0\le x<y\le 5\times 10^5\)。
考虑倒过来想,每一次操作是把 \([x,y]\) 切去一段区间 \([l,r]\),问多少次能切完。
定义 \(f_i\) 代表切到只剩 \([x,i]\) 区间的最小代价。
对于每个区间 \([l,r]\),\(f_{l-1}:\;\le f_r+1\)
这是差分约束,可以跑 dijkstra
,复杂度 \(nm\log n\)
考虑优化,我们定义 \(f_i\) 代表从 \([1,n]\) 切到只剩 \([1,i]\) 区间的最小代价。(如果图不联通,就分每个联通块单独跑,如果一个询问跨越多个联通块,那显然无解)
那么查询 \([x,y]\) 的答案就是 \(\max\left(f_{j \in \left[1,x\right)}\right)-\min\left(f_{i \in \left(y,n\right]}\right)\) ,可以前缀最大值,后缀最小值实现 \(O(1)\) 查询。
复杂度 \(O(m+n\log n)\)
看题解的做法更加天才,倍增求出每个点通过 \(2^q\) 个线段能到达的最远距离。
CF11D A Simple Task
求无向图中的简单环个数,保证不存在重边和自环。
简单环:除起点外,其余的点都只出现一次的回路。
\(n\le 19,0\le m \le \frac{n(n-1)}{2}\)
看数据范围是 \(2^n\) 的做法。
枚举点集 \(S\),\(O(n)\) 判断是否是合法简单环。复杂度 \(N_{O(n2^n)}=9961472\)
看看题解。
原来这种方法错误的,因为 知道点集 \(\not=\) 知道简单环。正确的做法是 \(f[x][S]\) 代表当前在 \(i\),经过的点集为 \(S\)。
CF86D Powerful array
给定长度为 \(n\) 的序列 \(a\),有 \(q\) 次询问,每次询问给出两个数 \(l,r\)。
对于每次询问,设 \(cnt_i\) 表示 \(i\) 在 \(a_l,a_{l+1},\cdots,a_r\) 出现的次数,您需要求出 \(\displaystyle\sum_i cnt_i^2\cdot i\)。
\(1\le n,q\le 2\times 10^5\),\(1\le a_i\le 10^6\),\(1\le l\le r\le n\)。
不会啊。
看看题解。
原来是分块或莫队。
CF165E Compatible Numbers
给定 \(n\) 个数 \(a_1,a_2,\dots,a_n\)
对每个 \(i\),求任意一个 \(j\) 使得 \(a_i\&a_j=0\)。如果不存在,输出 \(-1\)。
\(n\le10^6,1\le a_i \le 4\times 10^6\)
本质上是求是否有串满足 00*0**00*
类似的有 0
和通配符的格式。
所以这是一个 \(\log n\) 维空间找点的问题。
可以枚举每个状态 \(S\),设 \(f_S\) 代表 \(S\) 的子集里面任意一个 \(a\) 的下标,如果没有则为 \(0\) ,让 \(S:0\rightarrow (2^{\log n}-1)\) 更新,就可以做到 \(O(1)\) 查询。