二月刷题+复习

P10855 【MX-X2-T4】「Cfz Round 4」Gcd with Xor

暑假的题到现在终于看得懂题解了,看了一遍莫反推的式子感觉比较困难,还是高维差分好懂。

\[\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{i}\gcd(j,j\oplus i)^k=\sum\limits_{d=1}^{n}d^k\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{i}[\gcd(j,j\oplus i)=d] \]

也就是说我们对于每个 \(d\) 求出来有多少对 \((i,j)\) 满足 \(\gcd(j,j\oplus i)=d\) 就能求出答案,但是直接计数非常困难。

所以我们对于每个 \(d\) 求出有多少对 \((i,j)\) 满足 \(d\mid\gcd(j,j\oplus i)\) ,然后高维差分就能得到 \(\gcd(j,j\oplus i)=d\) 的数量。

求解 \(d\mid\gcd(j,j\oplus i)\) 的数对数量就容易了许多。

我们现在要求的是 \(\sum\limits_{d\mid j}^n\sum\limits_{i=j}^n[d\mid(j\oplus i)]\)。记 \(w(d,j,n)=\sum\limits_{d\mid j}^n\sum\limits_{i=0}^n[d\mid(j\oplus i)]\),那么我们要求的就是 \(\sum\limits_{d\mid j}^{n}w(d,j,n)-w(d,j,j-1)\)

对于每个 \(d\) ,我们枚举 \(d\) 的倍数 \(j\),从高到低枚举上界 \(n\) 的二进制位,计算 \(j\) 恰在当前二进制位小于 \(n\),后面随意的答案。由于这个是完整的二进制区间,那么他异或上一个数,还是一个完整的二进制区间,那我们只需要求一下区间内 \(d\) 的倍数的个数,这个可以 \(O(1)\) 实现。

P11660 我终将成为你的倒影

\(b\) 很小,这是我们做题的关键,容易想到对每个 \(b\) 预处理一些东西。

对于 \(a\) 我们只关注 \(a\bmod b\) 的值,后面的 \(a\) 都是在 \(\bmod b\) 意义下来说的。

对于两个数 \(x\ge y\) 满足 \(\lfloor\frac{x+a}{b}\rfloor=\lfloor\frac{y+a}{b}\rfloor\),那么只有两种情况:

  • \(\lfloor\frac{x}{b}\rfloor-\lfloor\frac{y}{b}\rfloor=1\),此时 \(a\) 要满足 \(b-y\bmod b\le a<b-x\bmod b\)
  • \(\lfloor\frac{x}{b}\rfloor-\lfloor\frac{y}{b}\rfloor=0\),此时 \(a\) 要满足 \(a<\min(b-x\bmod b,b-y\bmod b)\) 或者 \(a\ge\max(b-x\bmod b,b-y\bmod b)\)

这个区间加一操作可以差分实现。

这个解决了之后就可以分块对每个块预处理出 \(c_{i,b,a}\) 代表第 \(i\) 个块在 \(b,a\) 时的答案,问题就解决了。

P4921 [MtOI2018] 情侣?给我烧了!

这题二项式反演的复杂度是 \(Tn^2\) 的,碰到这种情况可以尝试把二项式反演的式子化一下。

\[\begin{aligned}f(k)&=\sum_{i=k}^{n}(-1)^{i-k}C_{i}^{k}C_{n}^{i}A_{n}^{i}(2!)^{i}(2n-2i)!\\&=\sum_{i=0}^{n-k}(-1)^{i}C_{i+k}^{k}C_{n}^{i+k}A_{n}^{i+k}2^{i+k}(2n-2k-2i)!\\&=\sum_{i=0}^{n-k}(-1)^{i}\frac{(i+k)!}{k!i!}\frac{n!}{(i+k)!(n-i-k)!}\frac{n!}{(n-i-k)!}2^{i+k}(2n-2k-2i)!\\&=\frac{2^{k}(n!)^2}{k!}\sum_{i=0}^{n-k}\frac{(-1)^{i}2^{i}}{i!}\frac{(2n-2k-2i)!}{((n-k-i)!)^{2}} \end{aligned} \]

发现后面的式子只和 \(n-k\) 有关了,这样就可以 \(O(n^2)\) 预处理,\(O(Tn)\) 查询了。

但这样并不能通过加强版。

\(g_i\) 表示 \(i\) 对情侣均错开的方案数,那么我们的 \(ans_k=\binom n k\times A_n^{k}\times2^k\times g_k\)

\(g_k\) 的求法可以类比错排:

  • 若原来有 \(i-1\) 对情侣均错开,现在新增了一对情侣和一排座位,显然这对情侣的其中一个可以任意选择位置,另一个只需要保证不和上一个人坐一排并且不把上个人换走的人的情侣换到一排就行,总方案数 \(2i\times2(i-1)\times g_{i-1}\)
  • 若原来有 \(i-2\) 对情侣均错开,有一对情侣坐在一排,首先那对情侣有 \(i-1\) 种可能,给这两对情侣分配哪一排有 \(A_i^2\) 种可能,最后这两对情侣相互交换错开的方案数为 \(8\),那么总方案数为 \((i-1)\times A_i^2\times8\).

最后得到我们的答案 \(g_i=4i(i-1)g_{i-1}+8i(i-1)^2g_{i-2}\)

Sloth

\(O(n^2)\) 比较容易想到,设 \(f_{x,0/1,0/1}\) 表示 \(x\) 子树根有没有匹配,除去根子树内有没有未匹配的点的方案数。之后枚举删去的每条边分别对两棵子树进行 dp。
这时候如果我们求出 \(g_{x,0/1,0/1}\) 代表删去 \(x->fa_x\) 这条边,\(fa_x\) 的子树内对应状态的方案数,就能实现换根 dp。
换根 dp 前后缀优化的基础是我们的 dp 要满足交换律和结合律,但是上方 dp 显然不满足。
究其原因是因为转移是儿子合并到父亲,乘法中两元的地位不等价,当然没有结合律交换律。
为了解决这个问题,我们试图先将儿子的 dp 值变成和父亲等价的然后再转移。

ARC Arc

把串串写出来 \(\texttt{CRARCRARC}\)

若初始 01 串全都为 0,当且仅当长度为 4 的倍数的时候合法。

剩下的情况至少有一个 1,那么我们要处理的就是被 1 分割开的极长 0 段。

对于极长 0 段,若其长度为偶数,那么没有任何影响,若长度为奇数,其实就是给两端的 \(1\) 加上一个限制(相同或不同)而这个限制是基于其 \(\bmod4\) 的余数求得的,使用扩展域并查集判断是否出环就好了。

Fennec VS. Snuke 2

猜测只和每堆的奇偶性有关,那我们记录 \(c_0,c_1\) 分别代表偶数堆和奇数堆的个数。

若有人选了倒数第二堆,那么另一个人直接把剩下的最后一堆取了就获胜了。

发现先手获胜当且仅当选的 \(n-2\) 堆和为奇数,失败则为偶数。

那么两个人的都有着明确的目的,最后肯定是轮流选一堆。所以枚举先手选的 \(1\) 堆个数,看后手是否有办法将最后的和变成 0 就好了。

Range Sums 2

\(P_1<P_2\) 基本把做法全盘托出了,对每个 \(i\) 查询 \(P_1\) 到它的和以及 \(P_2\) 到它的和,根据这个我们能判断这个数是在 \(P_1\) 左边还是右边。

上面确定之后,由于是正整数序列,可以左右两侧分别直接排序。这时候直接差分可以求出除了 \(P_1-1,P_1,P_1+1\) 这三个位置的值。

而这三个位置只需要最后单独讨论一下就好了。

某场联考 C

求期望生成树的个数,直接把每条边的概率当成边权使用矩阵树定理就可以了。
但是 \(T\) 很大,我们的复杂度不能带着它。
\(i\) 条边第 \(j\) 天的概率为 \(p_i+\frac{j}{k_i}\),所以把天数当成变量 \(x\),最后就是在这 \(n\) 个多项式中选 \(n-1\) 个相乘,相乘之后形成的多项式 \(f(x)\),暴力做的话就是把 \(1\sim T\) 全部代入进去。但是这时候对于这个多项式每一项,其实就是求 \(\sum\limits_{i=1}^{T}i^k\),这个是自然数幂和,可以拉格朗日插值。

某场联考 A

当时写的总结有点肤浅了,感觉像是贺的,重新想了一遍,非常困难。
对于修改次数 \(>\sqrt n\) 的下标比较好处理,直接 \(dfs\) 整棵树求出每个点的颜色,时间复杂度 \(O(n\sqrt n)\)
修改次数 \(\le\sqrt n\) 的是重点。
考虑如果修改次数只有两次,这个下标造成贡献当且仅当这两次修改不互为祖先关系,而且这两次修改的颜色不同,设这两次修改的点分别为 \(u,v\),那么这个下标会对 \((l,r)\) 满足 \(l\in[dfn_u,dfn_u+siz_u-1],r\in[dfn_v,dfn_v+siz_v-1]\) 的询问造成贡献,把询问挂在 dfn 序小的那个点,按 \(dfn\) 枚举点之后使用树状数组就可以维护了。
如果修改次数变多,先将进行修改的关键点按 dfn 从小到大排序,\(O(B^2)\) 枚举点对按上述方法处理贡献,这时候发现有的会被算重,这时候我们要用一些技巧来避免算重。
首先对于这些关键点我们处理出每个关键点在这些关键点中的父亲,后面再预处理一些东西。
我们枚举一个点 \(u\),同时枚举 dfn 序比他大的点 \(v\),对于每个点对,我们找出在这个点对 LCA 上方的第一个关键点,如果这个关键点和这个点对中任意一点的颜色相同且 \(v\) 子树内现在有贡献,那么删去。如果两两不同且 \(v\) 子树内现在没有贡献,那么添加。这里同时记录一下在 \(u\) 点时会对 \(v\) 子树造成加一或减一的贡献。
这个预处理完之后直接从根开始搜索就好了。

点击查看代码
void D4(int x)
{
	if(ve[idx[x]].size() <= B)
	{
		for (auto u : ve[idx[x]])
		{
			if(dfn[u] > dfn[idf[x]])
			{
				if(ad[idf[x]].count(u)) A(dfn[u],-1),A(dfn[u]+siz[u],1);
				if(del[idf[x]].count(u)) A(dfn[u],1),A(dfn[u]+siz[u],-1);
			}
		}
		for (auto u : ve[idx[x]])
		{
			if(dfn[u] > dfn[x])
			{
				if(ad[x].count(u)) A(dfn[u],1),A(dfn[u]+siz[u],-1);
				if(del[x].count(u)) A(dfn[u],-1),A(dfn[u]+siz[u],1);
			}
		}
	}
	for (auto [u,v] : ch[x]) ans[v] += Q(dfn[u]);
	for (int i = las[x],y;i;i = e[i].pre)
		if((y = e[i].to) != fa[x]) D4(y);
	if(ve[idx[x]].size() <= B)
	{
		for (auto u : ve[idx[x]])
		{
			if(dfn[u] > dfn[idf[x]])
			{
				if(ad[idf[x]].count(u)) A(dfn[u],1),A(dfn[u]+siz[u],-1);
				if(del[idf[x]].count(u)) A(dfn[u],-1),A(dfn[u]+siz[u],1);
			}
		}
		for (auto u : ve[idx[x]])
		{
			if(dfn[u] > dfn[x])
			{
				if(ad[x].count(u)) A(dfn[u],-1),A(dfn[u]+siz[u],1);
				if(del[x].count(u)) A(dfn[u],1),A(dfn[u]+siz[u],-1);
			}
		}
	}
}
int main()
{
	for (int i = 1;i <= m;i++)
	{
		ve[i].pb(0);
		sort(begin(ve[i]),end(ve[i]),[](int a,int b){return dfn[a] < dfn[b];});
		if(ve[i].size() > B)
		{
			D3(0,i);
			for (int j = 1;j <= q;j++)
				if(typ[que[j].u] != typ[que[j].v] && typ[que[j].u] != typ[que[j].l] && typ[que[j].v] != typ[que[j].l]) ans[j]++;
		}
		else
		{
			for (int j = 0;j < ve[i].size();j++)
			{
				for (int k = j-1;k >= 0;k--)
				{
					int x = ve[i][j],u = ve[i][k],l = LCA(x,u);
					if(u == l) {idf[x] = u;break;}
				}
				int tmp = ve[i][j];
				for (int k = j+1;k < ve[i].size();k++)
				{
					int x = ve[i][j],u = ve[i][k],l = LCA(x,u);
					if(x == l || u == l) continue;
					while(tmp != LCA(tmp,l)) tmp = idf[tmp];
					if(Q(dfn[u]) && (val[u] == val[x] || val[u] == val[tmp] || val[x] == val[tmp])) A(dfn[u],-1),A(dfn[u]+siz[u],1),del[x][u] = 1;
					if(!Q(dfn[u]) && val[u] != val[x] && val[u] != val[tmp] && val[x] != val[tmp]) A(dfn[u],1),A(dfn[u]+siz[u],-1),ad[x][u] = 1;
				}
				for(int k = j+1;k < ve[i].size();k++)
				{
					int x = ve[i][j],u = ve[i][k];
					if(ad[x].count(u)) A(dfn[u],-1),A(dfn[u]+siz[u],1);
					if(del[x].count(u)) A(dfn[u],1),A(dfn[u]+siz[u],-1);
				}
			}
		}
	}
	D4(0);
	return 0;
}

P3749 [六省联考 2017] 寿司餐厅

题意其实就是一个序列让你选若干个不交的子段,使得贡献减代价最大。
不过建模比较新颖,确实不算套路,最大权闭合子图。一个是选择区间 \([l,r]\) 会将 \([l,r]\) 所有子区间的贡献全加上,所以对于 \([l,r]\) 我们向 \([l+1,r]\)\([l,r-1]\) 连边。另一个是代价,我们分成两部分,一部分是每选一个付出 \(c\) 的代价,这个直接给 \(d_{i,i}\) 对应节点减去这个代价就好,另一部分是如果一个种类被选了至少一个,那么付出 \(mx^2\) 代价,这个同样对于种类为 \(x\) 的点向这个点连边,然后这个点的代价为 \(mx^2\) 就好。

posted @ 2025-02-08 08:01  ZepX_D  阅读(35)  评论(0)    收藏  举报