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

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

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

「其實沒有那麼特別。

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

我就是我。

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

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

宣洩不滿不甘心不認同,

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

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

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

追尋我自己。」

1. CF1698G Long Binary String

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

2. CF1562F Tubular Bells

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

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

3. CF516E Drazil and His Happy Friends

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

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

  • x(x+n)modm,边权为 n
  • 初始时 x 女感染,则源点 Sx 边权为 x
  • 初始时 x 男感染,则源点 Sxmodm 边权为 x

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

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

4. CF1225G To Make 1

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

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

那现在就是考虑是否有个序列 pi 满足 aikpi=1.套路就是 dp 令 fS,i 为集合 S 是否能够合并出 i,观察出如何转移不太容易,但似乎是这个题

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

这些过程可以看成两个操作:加入一个数 / 总和除以 k.所以可以列出 dp 转移:fS,x=fS,xk(iSfS{x},xai).用 bitset 优化,输出方案直接找到一个可行路径,然后根据构造方法模拟即可。时间复杂度:O((2nna)/ω+2na).

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

5. 【数据删除】

orz kdw 爆标。对所有 i,按照 minjfi,j 为顺序去松弛其他所有 fk,?,直接 O(m) 双指针一下就可以。时间复杂度 O(n2m)

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

假设是从 fx,i 调大到 fx,k,那么在我们的方案中代价是 fx,i+CkCi,在绕一圈的方案中是 fy,j+Ck,由于 fx,i=minfx,?minfy,?fy,j,所以前者一定小于等于后者,于是不会算错。

6. CSP-S2020 函数调用

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

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

7. CSP-S2019 树的重心

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

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

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

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

做法一 Code

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

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

(i=0m1(1+xi))n(modxm1)

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

fk=(i=0m1(1+ωmik))n=(i=0m/d1(1+ωm/di))dn

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

i=0m/d1(1+ωm/di)=(1)m/di=0m/d1(1ωm/di)=(1)m/d((1)m/d1)=[m/dmod2=1]2

于是 fk=[m/dmod2=1]2dn,这里发现 fk 的值仅和 d 有关,那么在 idft 的时候就可以利用这个转成枚举 d

gk=1mi=0m1fkωmik=1mdmfdi=0m/d1[gcd(i,m/d)=1]ωm/dik=1mdmfdi=0m/d1ei,em/dμ(e)ωm/dik=1mdemfdμ(e)0i<m/deωm/deik=1mdemfdμ(e)mde[mdek]

我朴素实现了个 O(m(logm+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;
    }
};

9. P5155 [USACO18DEC] Balance Beam P

dpi=max(fi,12(dpi1,dpi+1)),性质是 dpi12(dpi1+dpi+1),这就是凸函数的定义,于是猜测 dpi 是凸的,其就是 f 构成的凸包。注意要向下取整而负数是向零取整,所以有的计算方法不能直接整除需要 ceil

Code

10. P5156 [USACO18DEC] Sort It Out P

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

Code

11. P5157 [USACO18DEC] The Cow Gathering P

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

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

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

Code

12. P4183 [USACO18JAN] Cow at Large P

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

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

Code

13. P4184 [USACO18JAN] Sprinklers P

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

这里的 key point 是同一个方向的 2-side 矩形并是个楼梯状,从而观察出两个对角方向 2-side 矩形并的交具有的性质。

Code

14. CF1887D Split

怎么这种题都不会...状态太差了。大概有啥位置是关键的尝试枚举一下,去枚举左半部分的最大值 ai 就行。具体而言:令 xai 左侧最近的 ax>ai,那么 x<li,令 y1ai 右侧最近的 ay1>aiy2y1 右侧最近的 ay2<ai,然后 y1r<y2(推推就推出来了)。

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

Code

15. QOJ 7606 Digtal Nim

做法和 CodeChef MAXDTREE 一样。感觉和进制有关的可以往这方面想。nfls 地址。review 这个题的时候可以先看看这道。

dp 出 f[i][j][k] 表示 i 之上十进制位数位和 presum 为 ji 及以下均为 0;上一个 0 距离当前数 last0 为 k;存的值为将第 i 位增加 1 之后 last0 变成了多少。首先 f[0] 是好预处理的。

再处理出辅助数组 g[i][j][k][l],让第 i 位从 0 变成 l 之后 last0 变成了多少。转移 f[i] 的时候先用 f[i1] 将第 (i1) 位变成 9,这样能把 g[i1] 处理出来。但是下一步不能直接用 f[i1] 来变换 last0 得到 f[i][j][k],因为会产生进位影响到数位和,在第 (i1) 位上的数不再能简单视为给 presum 加上 l

(写出来发现错了对着手玩一下就能理解为啥)

所以用 gi2 的那些位都变为 9,得到 ?099999 这样一个数及其 last0,可以直接算出 ?100000 的 last0 也就是我们想要的。

check 就从大到小填进去 n 的每一位,看最后 last0 是不是变成了 0。其实我们就是处理出了一个填数的自动机。

Code

16. P6010 [USACO20JAN] Falling Portals P

life is hard/ll

先抽象成若干 y=akxxk 直线,问题变成从 (0,ai) 开始往右走直线最早什么时候走到 qi 的直线。

aq[i]<ai 的话应当尽可能用更快的下去,aq[i]>ai 则是用更慢的,两种情况对称先看前面那种。减少一些信息,aj<ai 的直线是无用的,肯定更慢。发现对于 ajai 的直线只有上凸包上的有用,求出 qi 与上凸包的交点即为答案。这里可以直接将直线对偶成点 (k,ak) 然后求点构成的下凸包,这里求凸包的时候可以直接用一个栈,因为是按照纵坐标从大到小插入的,相当于正常求凸包转了个 90 度。

qi 与对偶前凸包的交线,由于 aqi<ai,所以与它交的线就是假装在凸包中加入它没能弹出栈的那个线,二分求出来即可。

Code

17. P6144[USACO20FEB] Help Yourself P

普通幂展下降幂,然后现在要 dp 出选了 i 个连通块的方案。还是考虑扫描线,做个什么 dp。按 r 从小往大好不太好处理,因为可能会合并很多连通块。考虑按 l 从小往大扫,这样就记录 fi,j 表示最后一个连通块的右端点为 i,一共选出了 j 个连通块的方案数。

  • 对于 i<l 相当于 [l,r] 新开了一个连通块,分新的连通块是否选出来两种情况转移。
  • 对于 lir 没有新开,直接转移。
  • 对于 i>r,因为是按照 l 从小往大扫的,所以也没有新开一个连通块,那么所有方案可以分为 [l,r] 选或不选两种,直接将 fi,j ×2 即可。

Code

18. P7154 [USACO20DEC] Sleeping Cows P

这种匹配问题用括号来想会不那么抽象一点,从小到大排序之后奶牛看作左括号,牛棚看作右括号,需要对括号之间连出匹配使得《最左的失配左括号》不能在《最右的失配右括号》之前。这样就很好 dp 了。令 fi,j,0/1 表示考虑了前 i 个括号,有 j 个左括号需要匹配,是否出现了未匹配的左括号。转移是 O(1) 的,总时间复杂度为 O(n2)

Code

19. P7155 [USACO20DEC] Spaceship P

合法的按钮序列是一个大根笛卡尔树,于是应该是两段路径拼起来这样子合并,所以记 fi,j,k 表示 ij 最大值是 k 的方案数是多少。枚举 ij 上最大值取到的点是 x,转移是 fa,b,ifc,d,jfa,d,k,其中 k>max(i,j),且 (b,x),(x,c) 均存在。

下标上是 max 的卷积套路是作前缀和于是这里重新定义 fi,j,k 变成最大值 k 的方案数。枚举完 x 之后再令 g(a)=(b,x)Efa,b,k1h(d) 同理来辅助转移,那么现在就有 g(a)h(d)fa,d,k。还有根就在最边上的情况,那就是初始化 gx=hx=1

现在就优化到了 O(n4)。再询问怎么处理,把 q 个询问看成 q 个特殊点,在改一改对 f 赋初值的那个地方就行。

Code

posted @   do_while_true  阅读(97)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?

This blog has running: 1845 days 1 hours 33 minutes 17 seconds

点击右上角即可分享
微信分享提示