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}\) 不升的最小代价。

image

这种情况下虽然 \(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]\)):

image

image

image

没错,永远是个下凸包!

所以只用维护凸包就行了。

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\) 中出现的次数是一样的。

证明:不会

所以

\[\begin{align} f_i &= \frac{\left\vert\left\{L\cdots\vert i\in L \right\}\right\vert}{\left\vert\left\{L\cdots\right\}\right\vert}\\ &= \frac{\left\vert A_i\left\{i\in A_i\right\}\right\vert}{\left\vert\left\{A_i\right\}\right\vert}\\ &=\frac{1}{\text{dep}_i} \end{align} \]

所以 \(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)\) 查询。

posted @ 2024-11-06 22:04  DZhearMins  阅读(5)  评论(0编辑  收藏  举报