2023.10 ~ 夜已承载心无眠 再巨大的伤悲皆已成灰

发布时间 2023-10-28 11:38:35作者: do_while_true

https://www.bilibili.com/video/BV1yX4y1P7Kd

「其實沒有那麼特別。

我不想把自己定義在樂觀或悲觀的圈圈裡

我就是我。

我想成為一個追尋所愛就能投注心力的人,

即便最後沒有如願,我也會大哭一場,

宣洩不滿不甘心不認同,

再好好記住這份「得來不易」的回憶。

我知道,我沒有那麼特別。

但沒關係,我會很有彈性。

追尋我自己。」

1. CF1698G Long Binary String

假设最前面最后面都是 1,那么最前面一次操作的开头和最后一次操作的结尾一定不能被抹去,所以答案一定是 \(1\) 和几。一次操作用系数模 2 的多项式去表示它,记字符串的 gf 是 \(S(x)\),操作相当于每次加上 \(x^pS(x)\),要凑出 \(1+x^k\) 并且 \(k\) 尽可能小。这里可以直接列出来 \(x^k\equiv 1\pmod {S(x)}\),然后 bsgs 求解。这里的运算同 2023.9-8 那一题。还需要知道 \(k\) 的上界是多少,\(1,x,x^2,\cdots\) 连出来一定是一个环(同时这也说明了有 1 则一定有解),所以 \(k\) 最大不超过域的大小 \(2^{|S|-1}\)

2. CF1562F Tubular Bells

先考虑咋得到一个数,区间长度比较大来保证随机化可行,就不断随机另一个数和它询问,将问出来的 lcm 取 gcd 即可。没想到的地方是考虑完这个去考虑咋通过某个数把所有数求出来,这里考虑找到一个 \(>r/2\) 的质数 \(p\),这样 \(\text{lcm}(p,x)\) 一定是 \(px\) 除以 \(p\) 就得到了 \(x\)。而素数分布大致是 \(\log n\) 级别的,那么这里随机几百次很容易就随机到一个合法的。

区间长度比较小的时候把所有 \(\frac{n(n-1)}{2}\) 种可能都问一遍,\(a_i\) 就是 \(\gcd\{\text{lcm}(a_i,a_j)\}\),考虑证明 \(\gcd(\text{lcm}(x,x+1),\text{lcm}(x,x+2))=x\),因为相邻两个奇数/自然数一定互质,相邻两个偶数 gcd 为 \(2\) 然后分讨一下,同理 \(\gcd(\text{lcm}(x,x-1),\text{lcm}(x,x-2))=x\)。所以只有区间长度 \(=3\) 的时候不行,这个时候直接枚举所有可能的情况然后 check。

3. CF516E Drazil and His Happy Friends

只有模 \(d=\gcd(n,m)\) 相同能够互相感染所以 \(d>b+g\) 直接判为无解。单独看模 \(d=i\) 的那一组内部要花 \(j\) 的时间合法,那么这一组就需要 \(jd+i\) 的时间合法。现在考虑每一组内部怎么求:

考虑单独只看女生的最早全部感染时间,第 \(i\) 天时 \(i\bmod m\) 女让 \(i\bmod n\) 男感染,这个男的下一次进行感染是 \(i+n\) 天,会让 \((i+n)\bmod m\) 女感染。也就是 \(x\) 女会在 \(n\) 天后间接感染 \((x+n)\bmod m\) 女。从而想到最短路建图:

  • \(x\to (x+n)\bmod m\),边权为 \(n\)
  • 初始时 \(x\) 女感染,则源点 \(S\to x\) 边权为 \(x\)
  • 初始时 \(x\) 男感染,则源点 \(S\to x\bmod m\) 边权为 \(x\)

\(dis\) 的最大值即为所求。这张图长得太漂亮了,第一类边将 \(m\) 个点连成了一个大环,\(S\) 到达的点这些特殊点排列在这些环上,对于两个相邻特殊点 \(x\rightsquigarrow y\) 它们之间 dis 的最大值一定取到 \(y\) 前面那个点,通过 exgcd 得到每个特殊点排在第几位然后算那个值是啥就可以了。

没有想到的切入点是单独考虑某一方的最大时间从而转化到最短路问题。

4. CF1225G To Make 1

之前写过题解但是实现的时候依然发现了之前写的有一些笔误。

如果直接考虑每次删俩数再添进去一个是比较困难的,因为不好记录添进去之后的数。所以要考虑每个数最后的贡献,一定是 \(\frac{a_i}{k^{p_i}}\) 的形式。那就考虑什么样的 \(p_i\) 是合法的,首先一个必要条件是 \(\sum \frac{a_i}{k^{p_i}}=1\),然后发现其也是充分的,只需要每次找 \(p_i\) 最大的两个数合并起来就行,因为 \(\sum \frac{a_i}{k^{p_i}}=1\) 是个整数,那么 \(p_i\) 最大的那些 \(a_i\) 的和一定是 \(k\) 的倍数,否则就凑不出整数了。

那现在就是考虑是否有个序列 \(p_i\) 满足 \(\sum\frac{a_i}{k^{p_i}}=1\).套路就是 dp 令 \(f_{S,i}\) 为集合 \(S\) 是否能够合并出 \(i\),观察出如何转移不太容易,但似乎是这个题

考虑充分性的那个构造,每次选出一些数 \(a_i\),删除它们之后将它们总和除以 \(k\) 加入到集合中,再让它们的 \(p_i\)\(1\)(也就是构造过程中,让 \(p_i=\max p=m\) 的合并到一起,然后 \(m\gets m-1\)).再不断重复这个过程。

这些过程可以看成两个操作:加入一个数 / 总和除以 \(k\).所以可以列出 dp 转移:\(f_{S,x}=f_{S,x\cdot k}\cup (\cup_{i\in S}f_{S\backslash\{x\},x-a_i})\).用 bitset 优化,输出方案直接找到一个可行路径,然后根据构造方法模拟即可。时间复杂度:\(\mathcal{O}((2^n\cdot n\sum a)/\omega+2^n\sum a)\).

这个题的技巧就在于将这个复杂的结构转化成了 \((a_i,p_i)\) 构成的序列,而对于后者的处理技巧我们是熟悉的。

5. 【数据删除】

orz kdw 爆标。对所有 \(i\),按照 \(\min_j f_{i,j}\) 为顺序去松弛其他所有 \(f_{k,?}\),直接 \(\mathcal{O}(m)\) 双指针一下就可以。时间复杂度 \(\mathcal{O}(n^2m)\)

为什么是对的?算错当且仅当对于两个木桩 \(x,y\),它们都在我们求出的最短路上且 \(dis_x<dis_y\),但是答案的最短路 \(y\)\(x\) 前面。考察这两种方案走到 \(x\) 时是怎样的,后者相当于绕了个弯走了更远的路,带来的优势一定是让 \(x\) 的盘子更大些,我们断言在我们的方案中直接将 \(x\) 的盘子调大会不劣。

假设是从 \(f_{x,i}\) 调大到 \(f_{x,k}\),那么在我们的方案中代价是 \(f_{x,i}+C_k-C_i\),在绕一圈的方案中是 \(f_{y,j}+C_k\),由于 \(f_{x,i}=\min f_{x,?}\leq \min f_{y,?}\leq f_{y,j}\),所以前者一定小于等于后者,于是不会算错。

6. CSP-S2020 函数调用

我咋感觉这么难啊/ll 考虑倒过来,那就是维护一个 tag \(t\),如果是乘法操作就 \(t\gets t\cdot v\),如果加法操作就 \(a_x\gets a_x+tv\)。有了三操作,把 DAG 连出来,首先处理出来 \(f_x\) 表示操作一次 \(x\) 会使得 \(t\) 乘上啥,然后就不会做了/kel

\(t\) 实际上代表的是这个节点的操作次数会乘 \(t\),那么只需要求出所有加法操作的贡献系数就行了。从后往前扫的过程中维护 \(t\),对所有节点 \(x\) 记录它给所有后继加法系数的贡献次数相当于操作了 \(g_x\)\(x\).最后在 DAG 上利用 \(f\)\(g\) 递推出来就行。

7. CSP-S2019 树的重心

这题有点葡葡糖了。第一个是贡献转到点上算,后两个直接找出两侧的重心。

做法一:考虑一个点 \(x\) 作为重心的贡献次数,考虑断哪条边能使得 \(x\) 为重心,假设 \(x\) 各个邻点那部分子树的 size 最大值是 \(mx\) 次大值是 \(nx\),要割掉子树大小为 \(siz\),根据是不是在 \(mx\) 中砍的得到限制的两位是 \(siz\) 以及 dfn,所以二维数点就可以了。然后割的边在根链上的那部分特殊处理一下。

做法二:考虑找重心的一个方法是不断往 siz 最大的子树走直到 \(mx\leq n/2\),所以先预处理出倍增数组 \(f_{x,i}\) 表示 \(x\) 按照这个策略往孩子跳 \(2^i\) 步能跳到的点。判断 \(siz_v\)\(\frac{siz_x}{2}\) 的关系来确定跳多了还是跳少了还是跳到了。可能有两个点满足所以跳到较深的那个然后再判断其父亲是否满足条件。这样就把所有子树的给求出来了,还要求子树补的,换根一下就可以了。

做法三:发现子树重心是很好求的,可以直接记录 \(f_x\) 表示 \(x\) 子树的重心,然后其一定是重儿子的 \(f_v\) 不断跳父亲得到,暴力跳均摊就是 \(\mathcal{O}(n)\) 的。需要求出子树补重心,联想到 TEST_68 (虽然没想明白有什么巧合让这两个看起来毫不相关的问题可以用同一个 trick),先以整棵树的重心为根,求出所有子树重心,现在需要求子树补重心。考虑根的轻子树中割掉一个子树 \(v\),重心一定是在根所在的重链中,对于这条重链中每个点能作为谁的重心是对 \(siz_v\) 作出了限制,扫一遍就行。如果割掉的是重儿子的子树,那么重心一定是根的次重儿子所在重链中,也是扫一遍就行了(这个次重儿子 \(>1\) 个那么重心依然是根)。

做法一 Code

8. CF765G

判断 gcd 是否为 1 那就和质数具体次幂是啥无关,只需要知道 \(k\bmod p_i=r_i\) 的所有 \(r_i\) 即可知道这个 \(k\) 是否满足条件,另一方面由 CRT 也可指 \([0,N')\) 之间有唯一符合条件的 \(k\),其中 \(N'=\prod_{i=1}^n p_i\)。所以求出 \(N'\) 的答案之后乘上 \(\prod_{i=1}^np_i^{\alpha_i-1}\) 就行。

假设对每个 \(p_i\) 确定了一个 \(r_i\),让 \(k+r_i\equiv 0\pmod {p_i}\),那么这个 \(r_i\) 会让 \(s\)\(r_i,r_i+p_i,r_i+2p_i\cdots\) 都置为 \(0\)

to be continued

9. leetcode 「10·24」程序员节编程竞赛 计算子集

\(k\) 是没有用的求出 \(nm\) 的答案再把多出来的那 \(k\) 个物品每个暴力 \(\mathcal{O}(m)\) 合并进去就行。那么现在就是求:

\[\left(\prod_{i=0}^{m-1}(1+x^i)\right)^n\pmod {x^m-1} \]

循环卷积要做 dft,这里思路 和 UOJ 310 黎明前的巧克力 相同,UOJ 310 是直接把 fwt 写下来,这里来试着写一些 dft,\(f_k\) 即为代入 \(\omega_m^k\) 的值,这里令 \(d=\gcd(k,m)\)

\[\begin{aligned} f_k&=\left(\prod_{i=0}^{m-1}(1+\omega_m^{ik})\right)^n\\ &=\left(\prod_{i=0}^{m/d-1}(1+\omega_{m/d}^{i})\right)^{dn} \end{aligned} \]

这一步是因为 \(i\)\(m/d\) 相同的 \(\omega_m^{ik}\) 值是一样的(单位根的定义),这样化简之后发现指数仅和 \(i\) 有关了,括号里面的形式非常好看,考虑比较经典的等式 \(\prod\limits_{i=0}^{m-1}(x-\omega_m^i)=x^m-1\)(左右两侧均为 \(m\) 次多项式且 有 \(m\) 处点值相同且 \([x^m]\) 均为 \(1\) 故两式相等),将其代入 \(x=-1\) 就有:

\[\begin{aligned} &\prod_{i=0}^{m/d-1}(1+\omega_{m/d}^{i})\\ =&(-1)^{m/d}\prod_{i=0}^{m/d-1}(-1-\omega_{m/d}^i)\\ =&(-1)^{m/d}((-1)^{m/d}-1)\\ =&[m/d\bmod 2=1]2 \end{aligned} \]

于是 \(f_k=[m/d\bmod 2=1]2^{dn}\),这里发现 \(f_k\) 的值仅和 \(d\) 有关,那么在 idft 的时候就可以利用这个转成枚举 \(d\)

\[\begin{aligned} g_k=&\frac{1}{m}\sum_{i=0}^{m-1}f_k\omega_m^{-ik}\\ =&\frac{1}{m}\sum_{d\mid m}f_d\sum_{i=0}^{m/d-1}[\gcd(i,m/d)=1]\omega_{m/d}^{-ik}\\ =&\frac{1}{m}\sum_{d\mid m}f_d\sum_{i=0}^{m/d-1}\sum_{e\mid i,e\mid m/d}\mu(e)\omega_{m/d}^{-ik}\\ =&\frac{1}{m}\sum_{de\mid m}f_d\mu(e)\sum_{0\leq i<m/de}\omega_{m/de}^{-ik}\\ =&\frac{1}{m}\sum_{de\mid m}f_d\mu(e)\frac{m}{de}[\frac{m}{de}\mid k]\\ \end{aligned} \]

我朴素实现了个 \(\mathcal{O}(m(\log m+d(m)))\)

代码
class Solution {
public:
	typedef long long ll;
	typedef vector<int>vi;
	#define pb emplace_back
	static const int mod=998244353;
    static const int N=100010;
	inline void cadd(int &x,int y){x=(x+y>=mod)?(x+y-mod):(x+y);}
	inline void cdel(int &x,int y){x=(x-y<0)?(x-y+mod):(x-y);}
	inline int add(int x,int y){return (x+y>=mod)?(x+y-mod):(x+y);}
	inline int del(int x,int y){return (x-y<0)?(x-y+mod):(x-y);}
	int qpow(int x,ll y){
		int s=1;
		while(y){
			if(y&1)s=1ll*s*x%mod;
			x=1ll*x*x%mod;
			y>>=1;
		}
		return s;
	}
	ll n,m,k;
	int vis[N];
	vi pr;
	int f[N],g[N];
	vi vec[N];
	void init(){
		for(int i=2;i<=m;i++){
			if(!vis[i])
				pr.pb(i);
			for(auto j:pr){
				if(i*j>m)break;
				vis[i*j]=1;
				if(i%j==0)break;
			}
		}
	}
    ll gcd(ll x,ll y){return !y?x:gcd(y,x%y);}
    vector<int> subsetCounting(long long q, int w, int e) {
    	n=q;k=w;m=e;
		if(n){
			init();
			for(int i=1;i<=m;i++){
				int d=__gcd(1ll*i,m);
				if(m/d%2==1){
					f[i]=qpow(2,1ll*d*n);
				}
			}
			for(auto i:pr){
				for(int j=m/i;j;j--)
					cdel(f[i*j],f[j]);
			}
			for(int i=1;i<=m;i++)if(m%i==0)vec[m].pb(i);
			int iv=qpow(m,mod-2);
			for(int i=1;i<=m;i++){
				for(auto de:vec[m]){
					if(i%(m/de)==0){
						cadd(g[i],1ll*f[de]*(m/de)%mod);
					}
				}
				g[i]=1ll*g[i]*iv%mod;
			}
			g[0]=g[m];
			g[m]=0;
		}
		else g[0]=1;
		for(int i=0;i<=m;i++)f[i]=0;
		for(int i=1;i<=k;i++){
			for(int j=0;j<m;j++)
				cadd(f[(j+i)%m],g[j]);
			for(int j=0;j<m;j++)
				g[j]=add(f[j],g[j]),f[j]=0;
		}
		vi vec;
		for(int i=0;i<m;i++)vec.pb(g[i]);
		return vec;
    }
};

10. P5155 [USACO18DEC] Balance Beam P

\(dp_i=\max(f_i,\frac{1}{2}(dp_{i-1},dp_{i+1}))\),性质是 \(dp_i\geq \frac{1}{2}(dp_{i-1}+dp_{i+1})\),这就是凸函数的定义,于是猜测 \(dp_i\) 是凸的,其就是 \(f\) 构成的凸包。注意要向下取整而负数是向零取整,所以有的计算方法不能直接整除需要 ceil

Code

11. P5156 [USACO18DEC] Sort It Out P

首先没选出来的一定构成 IS 因为它们之间相对顺序不会变,其次《两个相邻的其中有个是S内的元素的逆序对》一定在减少所以证明了充分性。找 size 最小选出集合的第 \(k\) 大就是找 LIS 的第 \(k\) 大。贪心依次确定即可。

Code

12. P5157 [USACO18DEC] The Cow Gathering P

有一个思路是考虑 \(a\)\(b\) 先走等价于 \(b\) 为根时 \(a\) 的子树里面每一个点都在 \(b\) 前面,建出这个图 \(G\)\(G\) 中有环则无解。否则 \(G\) 中出度为 0 的就是可以最后走的。首先出度不为 0 的一定不行;如果点 \(x\) 出度为 0 那么可以构造出一个方案(把树以 \(x\) 为根,儿子连向父亲然后拓扑排序,因为 \(x\) 出度为 0 所以不会有祖先连向儿子故依然是个 DAG)。

直接线段树优化建图可行,看看怎么线性。只需判断出度是否有 \(0\) 可以在 dfs 序上差分。如果是 \(a\)\(b\) 的祖先还需要找 \(b\)\(a\) 的哪棵子树中可以离线 dfs 记录栈。

还需要判环。选一个出度为 \(0\)\(x\) 当根,这个时候发现限制里的 \((a,b)\) 一定是 \(a\) 子树所有点连向 \(b\),而已经儿子连向父亲所以只需连一条 \(a\to b\) 即可。这样 check 无解复杂度也是 \(\mathcal{O}(n)\) 的了。

Code

13. P4183 [USACO18JAN] Cow at Large P

一个叶子选中了之后能激活它的 \(dep/2\) 级祖先,然后所有最浅被激活点个数就是答案。这样做还是有些复杂,考虑每个点是否能成为被激活点,令 \(f[i]\) 表示距离 \(i\) 的最近叶子距离,那么它能被激活就是 \(f[i]\leq dep[i]\),还要满足父亲不被激活才能产生贡献于是还需满足 \(f[fa]>dep[fa]\)

这里贡献还是和 \(fa\) 有关,怎么去掉它就考虑一个类似树上差分的东西,将所有可激活点标出,叶子均赋值为 \(1\),对于某个点它能被激活子树也一定都能被激活,它的权值就是儿子个数。这样被激活的点的权值和就是答案。这个时候发现点权就是 \(2-deg_i\),于是 \(ans_x=\sum [dis(x,i)\geq f_i](2-deg_i)\),点分统计即可。

Code

14. P4184 [USACO18JAN] Sprinklers P

需要把图画出来找找合法格子的规律,发现是一个阶梯状(具体可以看洛谷题解)然后树状数组优化就行了很好写。Code

15. CF1887D Split

怎么这种题都不会...状态太差了。大概有啥位置是关键的尝试枚举一下,去枚举左半部分的最大值 \(a_i\) 就行。具体而言:令 \(x\)\(a_i\) 左侧最近的 \(a_x>a_i\),那么 \(x<l\leq i\),令 \(y_1\)\(a_i\) 右侧最近的 \(a_{y_1}>a_i\)\(y_2\)\(y_1\) 右侧最近的 \(a_{y_2}<a_i\),然后 \(y_1\leq r<y_2\)(推推就推出来了)。

另一个枚举做法能枚出来的做法是对值域扫描线,从下往上扫,扫到一个数就把位置标成 1 ,然后一个区间在某时刻 11110000 就行,对于每次 0 变 1 之后新产生的极长 11111000 段只有一个所以也是扫描线一下就行。

Code