一些萌萌题

下面是近期我见识到的一些新套路/oh

  • P1640 [SCOI2010] 连续攻击游戏 考虑值域是很小的,并且编号向权值连边不好做,那么直接正难则反!权值向编号连边,从权值 \(1\) 开始最大匹配,直到有一个权值无法到达。如果直接最大流 \(O(nE) = 1e4 \times 1e6\) 可能复杂度有点假,不过网络流一般是跑不满的,能过!注意清零的时候不能用 memset ,常数略大。

  • CF940F Machine Learning 这种求区间数出现的次数的问题有一个巧妙的性质:\(\sum_{i = 1}^{k}i = \frac{k(k+1)}{2} = n\) ,可得知这个答案 \(k\) 是一个 \(\sqrt n\) 级别的,对于莫队这种根号数据结构来说,暴力的复杂度是可以接受的。

  • CF375D Tree and Queries 第一次看我还以为跟上面这个题一样暴力算就行了,可是会出现一个子树内全是一个颜色的情况,我就成神笔了。上面这种 \(\sqrt n\) 级别的意思是不同的区间数出现的次数是 \(\sqrt n\) 级别的,而这个题让你求的是出现次数 \(\ge k\) 的数,不是让你统计不同的次数。这个题 \(O(1)\) 求贡献的办法也比较巧妙:设 \(Cnt_i\) 表示有多少颜色的出现次数 \(\ge i\)。于是代码就好写了。

    再者,对于链问题,往往用欧拉序解决问题,因为要消去不在链上的点的贡献;对于子树问题,往往用 dfs 序解决问题,记录下入栈和出栈的时间点,那么这两者之间的都算入贡献。

il void Add(int x)
{
	x = a[x];
	cnt[x]++;
	Cnt[cnt[x]]++;
}
il void Del(int x)
{
	x = a[x];
	Cnt[cnt[x]]--;
	cnt[x]--;
}
  • AcWing208.开关问题 一个异或高斯消元的题,但是做的时候由于对高斯消元理解不清,导致卡了很久。我采用的是高斯-约旦消元,就要凑成对角矩阵。而消 \(0\) 的过程中要加一句 if(a[k][i]) 后再消,要不然 0^1 = 1 ,白消了。而它左边的都是 0 ^ 0 = 0 没有影响。

  • P4550 收集邮票 其实做多了就是套路,倒推。不过我想写的是因为这位博主的题解非常细心和通俗易懂,让我豁然开朗。

  • P1291 百事世界杯之旅 期望DP,两种做法。

    \(\text{Method} \ 1\):首先有个结论:若一个事件的概率为 \(p\),那么期望它在 \(\frac{1}{p}\) 次后发生。难道就我不知道这个结论???菜的真实。

    然后设 \(f_i\) 表示从 \(i-1\) 个饮料转移到 \(i\) 个饮料的期望次数。抽到新饮料的概率是 \(\frac{n-i+1}{n}\),那么期望次数就是 \(\frac{n}{n-i+1}\)

    最后的答案就是对 \(f_i\) 求和,也就是一步一步往下转移。

    \[\begin{aligned} \text{ans} &= \sum_{i=1}^n\frac{n}{n-i+1}=n\sum_{i=1}^n\frac{1}{n-i+1}\\ &=n\sum_{i=1}^n\frac{1}{i} \end{aligned} \]

    \(\text{Method} \ 2\): 倒推!倒推!倒推!设 \(f_i\) 表示已经选出 \(i\) 种不同的饮料,还需选择的次数的期望值。由于是倒推,那么 \(f_i\) 可以由 \(f_i\) 转移过来,代表再选一次仍旧选中了已有的答案,转移就是 \(\frac{i}{n}\times(f_i+1)\);同时也可以由 \(f_{i+1}\) 转移过来,即再选一次选择了不同饮料,转移就是 \(\frac{n-i}{n}\times(f_{i+1}+1)\),总的加起来就是

    \[f_i = \frac{i}{n}\times (f_i+1) + \frac{n-i}{n}\times (f_{i+1}+1) \]

    移项,最后得到

    \[f_i = f_{i+1} + \frac{n}{n-i} \]

    \(O(n)\) 递推即可。

  • P5505 [JSOI2011]分特产 容斥入门题目,但是为什么我想了那么久?

思路是设 \(g_i\) 表示刚好 \(i\) 个同学没分到的方案数,并设 \(f_i\) 表示至少 \(i\) 个同学没分到特产的方案数。\(f_i\) 是好表示的,运用隔板法可以得出

\[f_i = C_n^i \times \prod_{j=1}^mC_{a[j]+n-i-1}^{n-i-1} \]

因为我们的答案是 \(g_0\) ,我们考虑如何用 \(f_i\) 表示出它来。

其实按照上述的公式计算出来的 \(f_i\) 会有重复,因为严格按照定义来的话 \(f_i = g_i + g_{i+1}+\dots\),但是这么一算的话会有重复,比如 \(n=3\),我选定 \(A\) 一定分不到特产,那么 \(B,C\) 也有可能分不到特产,这样,\(A,B\) 分不到的情况会在这里出现;同时,我假定 \(B\) 一定分不到特产,那么 \(A\) 也有可能分不到特产,\(A,B\) 分不到的情况也会在这里出现,所以会出现重复。

所以这样的 \(f_i\) 其实是不符合定义的,但是有利于我们去计算 \(g_0\)

我们思考 \(g_j\)\(f_i\) 中会重复几次:这其实相当于我找到 \(j\) 中的 \(i\) 个进行固定,表示这 \(i\) 个同学一定分不到特产,那么这样的方案数就是 \(C_j^i\),也就说明重复了 \(C_j^i\) 次。

那么从 \(f_0\)\(f_n\)\(g_i\) 其实重复了 \(C_i^0 + C_i^1 + \dots + C_i^i\) 次。

而根据公式

\[\sum_{i=0}^n(-1)^iC_n^i =0 \]

这个组合数的性质我们可以把重复的 \(g_i\) 容斥掉,这样最后只会剩下一个 \(g_0\) ,因为它不符合这个公式。

所以说最后的答案就可以写成

\[\sum_{i=0}^n(-1)^if_i \]

的形式。

让我理解了一上午的题目。

首先从恰好等于这方面入手,隐约提醒你可能是二项式反演。

于是,设 \(g_m\) 表示 \(n\) 个数中,恰好\(m\)\(a>b\) 的方案数。

\(f_m\) 表示 \(n\) 个数中,至少\(m\)\(a>b\) 的方案数。

其实换一种方式理解 \(f_m\) 更好:

\(f_m\) 表示 \(n\) 个数中,钦定某 \(m\) 对数 \(a>b\),剩下的不管的方案数。

那么有 \(\displaystyle f_m = \sum_{i=m}^n\binom{i}{m}g_i\)

我是这么理解为什么要乘上一个 \(\displaystyle \binom{i}{m}\) 的。\(g_i\) 表示 \(n\) 个数中,恰好有 \(i\)\(a>b\),而我从中钦定的 \(m\) 对中选 \(i\) 对,每一个都不一样,自然而然就是 \(\displaystyle \binom{n}{i}\)

这个 \(f_i\) 其实也没有之前的那么好求,得用 DP。

\(F_{i,j}\) 表示选到前 \(i\) 个数,钦定有 \(j\)\(a>b\) 的方案。考虑如何转移。

为了好转移,我们先给 \(a,b\) 数组排个序。

  1. 若不考虑第 \(i\) 个,那么就是前 \(i-1\) 个数选 \(j\) 个,方案数就是 \(F_{i-1,j}\)

  2. 若考虑第 \(i\) 个配对,那么就是前 \(i-1\) 个中选 \(j-1\) 个,我们再给个指针 pos 指到最大的小于 \(a_i\)\(b\) 的位置,那么第 \(i\) 个还可以选 \(pos-j+1\) 个,因为前 \(i-1\) 个已经选了 \(j-1\) 个了,这样方案数就是 \(F_{i-1,j-1}\times(pos-j+1)\)

加起来,就得到

\[F_{i,j} = F_{i-1,j} + F_{i-1,j-1}\times(pos-j+1) \]

我们可以看出,剩下的 \(n-j\) 对我们是没有管的,有可能 \(a > b\),也有可能 \(a < b\),因此我们就能求出 \(f_i\) 了,也就是

\[f_m = F_{n,m}(n-m)! \]

因为剩下的 \(n-m\) 个位置是任意配对的。

有了 \(f\),二项式反演出 \(g\),也就是答案即可。

时隔一年再做一次。

感觉这个题的重点不在斜率优化,而在于费用提前计算的思想。

朴素的 DP 是设 \(f_{i,j}\) 表示前 \(i\) 个机器分成 \(j\) 批的最小费用。然后发现这个转移是 \(\mathcal O(n^3)\) 的,因为这个 \(j\) 其实上界是 \(n\)。你会发现第二维存在的意义就是让我知道前面分了几个批次,因为 \(s\) 的存在,知道了几个批次才能知道现在的时间。然后就有了我们的费用提前计算的思想

思考,我们新开一个批次,那么它会对这个批次及以后,一直到最后的机器产生 \(s \times c_i\) 的费用,而对前面的批次没有影响。那我们就可以把这个费用从 DP 状态中提出来,给个前缀和 \(sumC_i\)\(sumT_i\),那么从 \(i+1\) 这台机器新开一个批次的贡献是 \(s \times (sumC_n -sumC_i)\),那么其余的部分就不需要考虑开了几台机器了,于是我们就只需要设 \(f_i\) 表示考虑到前 \(i\) 个机器的最小费用,那么就有:

\[f_i = \min_{0\leq j < i}\{f_j + s \times (sumC_n-sumC_j) + sumT_i \times (sumC_i-sumC_j)\} \]

然后就是拆项斜率优化,这个不是重点。

还有一个坑点就是这个题的一种斜率是 \(sumT_i\),另一种是 \(sumT_i + s\),但是你会发现这个题 \(|T_i| \leq 2^8\),也就是完成一个机器的时间可以为负??所以就不能维护一个单调队列的队头了,查询的时候用二分 \(\mathcal O(\log n)\) 查询即可,总时间 \(\mathcal O(n\log n)\)

  • 如果一个题当前点的贡献要考虑它之前点的类似于时间的贡献的 \(\sum\),正着做难做,可以考虑费用提前计算。

  • 网格图网络流题,如果是点与点之间横坐标或者纵坐标相同就用一些贡献的话,可以按照 \(S\rightarrow\) 行连通块 \(\rightarrow\) 列连通块 \(\rightarrow T\) 的思路来建图。

  • 二进制拆位计算

简化版,求 \(\displaystyle \sum_{i=1}^n\sum_{j=1}^n(a_i \oplus a_j)\)

当位数比较大的时候,我们显然有 \(O(n^2\log V)\) 的做法,但是我们可以优化到 \(O(n\log^2V)\)

\(m\) 为这 \(n\) 个数中转化到二进制下的最高位,这个式子就可以转化为

\[= \sum_{i=1}^n\sum_{j=1}^n\sum_{k=0}^m 2^k \times [bit_{i,k} \not= bit_{j,k}] \]

我们考虑枚举每个数 \(a_i\),现在的问题变成了我要看 \(j\in 1\sim n\) 中所有 \(a_j\)\(a_i\) 的贡献,那么答案显然就是

\[\sum_{k=0}^m 2^k \times \sum_{j=1}^n [bit_{i,k} \not= bit_{j,k}] \]

\(bit_{i,k} = 0\),那我们就是统计 \(j\in 1\sim n\) 中有多少数二进制位的第 \(k\) 位是 \(1\),则在这一位会产生 \(2^k \times cnt_1\) 的贡献;若 \(bit_{i,k} = 1\),那我们就是统计 \(j\in 1\sim n\) 中有多少数二进制位的第 \(k\) 位是 \(0\),则在这一位会产生 \(2^k \times cnt_0\) 的贡献;

所以我们只需要预处理 \(f[i,0/1]\) 表示 \(a_1 \sim a_n\) 中二进制下第 \(i\) 位是 \(0/1\) 的总数,这个预处理很显然是 \(O(n\log V)\) 的,然后统计答案的时候首先有一个枚举的 \(O(n)\),然后你会发现第二个式子里面的后面那个 \(\sum\) 我们已经预处理出来了,所以统计答案也是 \(O(n\log V)\) 的,所以最后复杂度就优化成了 \(O(n\log V)\)

然后对于 \(\displaystyle \sum_{i=1}^n\sum_{j=1}^n (a_i \oplus a_j)^2\) 也是同理的,但是要再乘个 \(\log V\),因为你需要设 \(f[x,0/1,y,0/1]\) 表示 \(a_1 \sim a_n\) 中有多少个数的第 \(x\) 位是 \(0/1\),并且第 \(y\) 位是 \(0/1\),因为你统计答案的时候柿子是这样的:

\[\sum_{x=0}^m\sum_{y=0}^m2^{x+y}(\sum_{i=1}^n\sum_{j=1}^n[bit_{i,x}\not= bit_{j,x}][bit_{i,y}\not = bit_{j,y}]) \]

这类极差选取问题,可以用这样一个套路:

看到极差首先想到对 \(w_i\) 排序,又发现只要不影响极差取区间是多多益善的

所以排完序后答案必然是在区间序列中一段连续的区间

使用双指针,枚举右端点覆盖 \(1\sim n\),然后移动左指针到临界点,更新答案即可,可以用双指针的原因是排序完是有单调性的。

每次移动的时候用线段树模拟一下联通情况即可,如果存在通路,那么充要条件就是每个点至少被覆盖一次,用一个区间加 & 区间最小值的线段树维护即可做到 \(O(n\log n)\)

  • 区间最小覆盖价值我们可以按 \(w\) 排序,如果求满足最多覆盖数量类型的答案那就按端点排序。

  • P1972 [SDOI2009] HH的项链

多测,区间数颜色,可以离线。

这题把所有询问离线下来,然后按右端点离线下来,然后对于若干个 \([l,r]\) 区间,如果他们的 \(r\) 都相等的话,那么我们数颜色时只需关心每个颜色中最靠右的颜色,我们依据这个建立树状数组就可以做了。对于每个点开一个 vector,存储以该点为右端点的区间,然后建立树状数组,把该点位置 \(+1\),该点所代表的颜色的上一个位置 \(-1\),然后树状数组统计一下颜色数目就可以了,时间复杂度 \(O((n+m)\log n)\)

NB 题。

首先最朴素的暴力是 \(O(q n^2\log n)\) 的,巨大超时。

引出我们的第一个优化:\(O(n\log n)\) 的时间内找序列的众数:序列排序后,我们发现可以二分求众数,设当前二分到了 \(x\),我们把 \(\ge x\) 的数设为 \(1\)\(<x\) 的数设为 \(-1\),每次判断序列总和是否 \(\ge 0\),如果是的话,说明众数在 \(x\) 的右边,反之就在左边,这样以后,单次询问的复杂度降为 \(O(n\log n)\),总时间复杂度就是 \(O(qn\log n)\),还是过不去。

然后是第二个优化:我们可以将整个序列排序,然后运用主席树,以 \(root_i\) 为根的那一棵线段树存储的信息就是以排序去重后 \(a_i\) 为根的 \(-1/1\) 的情况,你会发现从 \(a_{i-1}\)\(a_i\),只需要将 \(a_{i-1}\) 修改为 \(-1\) 即可,预处理 \(O(n\log n)\)

如果我们已知了 \([a,b],[c,d]\),然后假设我们二分到了 \(x\)。那么首先 \([b+1,c-1]\) 这一段是必选的,我们可以在主席树中统计一个 sum 来维护;然后我们发现,我们应该寻找 \([a,b]\) 的最大后缀和和 \([c,d]\) 的最大前缀和,这个做过 GSS4 的都知道该怎么维护了,然后这个题我们就能在 \(O(n\log n + q\log^2n)\) 的复杂度内做完了。

但是你可能还有一个问题:会不会二分出的那个答案不在区间里?不会的,因为如果区间外有一个数满足要求,那么区间内一定会有一个数的权值和大于等于它,可以举个例子看看。

并查集大失败,或者说我不会写。

平常的左偏树题需要从一个点开始跳到一个堆的堆顶,可是这个题不需要,它只在乎堆顶,不需要你跳,所以就不用并查集,然后就好写很多。

这个题的思想也比较牛,我们对于每一堆骑士维护一个可并堆(小根),从叶子不断往上跳,跳到一个新节点就判断堆顶的战斗力 \(s_i\) 是否小于防御力 \(v_i\),如果是,把它弹出,while 循环一下,然后继续往上跳。

然后这个过程因为有了 \(a_i,v_i\) 的存在导致这一堆骑士往上走战斗力会变,我们不能每次都给这些骑士改变值,这就跟暴力没区别了。怎么做呢?思考线段树的懒标记思想,每次只给堆顶打标记,先乘后加懒标记,每次 merge 还有一些别的操作的时候 push_down 给左右儿子就行了。

然后还有一个我觉得比较妙的点是 root[i] 表示的是第 \(i\)城池里面的士兵的堆,而不是第 \(i\) 个士兵在哪个堆里,虽然后面这个定义一眼假,但是我还是觉得这么设挺妙的。

code

  • 有时候要求一个递增序列有关的,可以令 \(a_i' = a_i - i\),转化为求一个不降序列有关的,不降序列通常比递增序列有着更好的性质。

  • P2899 [USACO08JAN] Cell Phone Network G

还是不会设树形 DP 的状态捏。

最初设了一个典型的 \(f_{x,0/1}\) 表示以 \(x\) 为根的子树中,这个点不放/放的最小代价,然后很轻松地推出了状态转移柿子,一发过了样例,然后 \(0\) 分!

错因是没有考虑到父亲,也就是说如果你 \(x\) 没有放,你的儿子也有可能都不放,因为你父亲可能放了,然后儿子的儿子可能放了,这样也是合法的,但是我没考虑到,寄了。

所以就设 \(f_{x,0/1/2}\) 表示自己放了/孩子放了/父亲放了,并且这个状态是合法的的最小代价,转移方程有:

\[f_{x,0} = \sum_{y\in son(x)} \min\{f_{y,0},f_{y,1},f_{y,2}\} \]

\[f_{x,2} = \sum_{y\in son(x)} \min(f_{y,0},f_{y,1}) \]

然后是比较难处理的 \(f_{x,1}\),因为 \(x\) 众多孩子中只要有一个是自己染的,那么 \(f_{x,1}\) 就合法,所以我们给个 flag,判断 \(f_{x,1}\) 是否包含着一个 \(f_{y,0}\);同时我们记录一个 diff,存储若 \(f_{y,1} < f_{y,0}\),也就是 \(f_{x,1}\)\(f_{y,1}\) 更新而来的时候,最小的 \(f_{y,0} - f_{y,1}\),这样,如果说 \(f_{x,1}\) 全被 \(f_{y,1}\) 更新的时候,我们就给 \(f_{x,1}\) 加上 diff,这样能够以最小的代价使得 \(x\) 被染色。

然后 \(f_{x,1}\)\(f_{x,2}\) 的柿子是一样的,只是 \(f_{x,1}\) 需要判断 diff

非常好题目,爱来自瓷器。

细数一下这个题我读题没读透的信息:

    1. 乡村一定是叶子节点,这个我读出来了;
    1. 每个城市一定恰好有一条铁路和一条公路连向它
    1. 由信息二我们可以知道这棵树是一个二叉树。
    1. 题目刚好就是给出每个点连向它的公路和铁路
    1. 每个城市恰好修铁路或公路的一条

有了这几个信息,我们再仔细思考思考,然后设出来这样一个状态:设 \(f_{x,j,k}\) 表示从根走到 \(x\) 点,通过了 \(j\) 条未修的公路,\(k\) 条未修的铁路,我们分情况考虑:

如果该点是乡村,那么直接 \(f_{x,j,k} = c_x(a_x+j)(b_x+k)\)

如果该点是城市,那么就又分两种情况考虑:

  • 修公路,那么如果 \(x\) 往下走铁路那一条道,未修道路会 \(+1\)。我们设 \(l_i\) 是走公路,\(r_i\) 走铁路,那么就是 \(f_{l_i,j,k} + f_{r_i,j,k+1}\)

  • 修铁路,那么如果 \(x\) 往下走公路那一条道,未修道路会 \(+1\),那么就是 \(f_{l_i,j+1,k} + f_{r_i,j,k}\)

这两种取 \(\min\) 即可。那么最后肯定答案就是 \(f_{1,0,0}\)

if(!dfn[y])
{
		tarjan(y);
		low[x] = min(low[x],low[y])
}
else if(...) low[x] = min(low[x],dfn[y])

有关 tarjan 的在此基础上删删改改/fn。

主要是来贴个我觉得为什么要建有向图的好懂的证明

  • 强连通分量如果要缩点并且和缩点后入度出度有关的时候,记得有可能会出现重边,比如一个 SCC1 中的一个点有向另一个 SCC2 中的一个点的连边,而 SCC1 中还有一个点也会向 SCC2 中的一个点连边。这样缩点完后这两条便其实是一条边,有些时候这个地方可能需要处理,有些地方不需要,看你要维护的是什么了。最好还是处理一下,可以用个 map<pair<int,int>,bool> Link 加个 \(\log\) 处理一下。

  • 模拟赛题:有关矩形的行列交换

有个很重要的性质就是矩形无论是先交换两行后交换两列还是先交换两列再交换两行,最终的结果是一样的。所以说多次交换整行整列我们只需要对每行每列打个标记,用 \(row_i\) 表示现在的第 \(i\) 行应该是初始的第几行,\(col_i\) 表示现在的第 \(i\) 列应该是初始的第几列,然后初始的时候 \(row_i = i , col_i = i\),于是交换的时候我们只需要 \(O(1)\) swap 一下即可。

  • 妈的不该放全局变量的东西你别放,每次 dfs 的时候要存储的东西就设在 dfs 内部/fn/fn/fn

  • P4926 [1007] 倍杀测量者

感觉是个自己之前见过的 trick 但是一时之间忘记了,怎么这么笨?首先你会发现答案具有可二分性,要不然这个 T 无穷无尽也枚举不完。然后你会发现这个操作一如果没女装,就相当于 \(W_A \ge W_b(k+T)\),操作二没女装相当于 \(W_A(k+T)\ge W_b\),发现都是大于号,有点像差分约束,我开始的时候是想拆一下变成 \(W_A \ge W_b+W_b(k+T-1)\),但是这个权值跟 \(W_b\) 有关,而有些 \(W\) 我们是未知的,所以不大好。

这时候我们发现这个式子是一个乘法的形式,我们通过给不等式两边取个 \(\log\),便可以转化为加法:\(\log(W_a)\ge \log(W_b) + \log(k+T)\) 这样的形式了,这样就可以进行差分约束了,然后对于已经确定 \(W\) 的点,我们让他和 \(0\) 对于 \(W_x \ge v,W_x \leq v\) 的连边,就是等于号转化成大于等于和小于等于就行了,其余的不连就相当于它们的值域是 \(0\) 到正无穷(初始 \(dis_i\) 都为 \(0\))。

然后这题因为 \(0\) 有一些用处,所以我借鉴了魏老师的写法,不是从 \(0\) 向每个点连一条边权为 \(0\) 的边,而是直接把 \(0\sim n\)\(dis_i\) 都设为 \(0\),并且让 \(0\sim n\) 都入队,这样就能过了。

一晚上做了三个差分约束好题,感谢魏老师。

首先我发现有环一定无解,然后取决于最长那条路径,然后我发现这些东西屁用没有,然后就不会了/cy。

最重要的一步就是转换思路,正难则反,与其设置边权使路径长度相等,不如设置路径长度去拟合边权的限制

\(d_i\)\(1\sim i\) 的最短路,我们只需保证对于所有在从 \(1\sim n\) 路径上的边 \((u,v)\),有 \(w_{u,v} = d_v - d_u\),即可使任意一条路径长相等,为啥呢?

\(w_{u,v} > d_v - d_u\),那么说明 \(v\) 存在一条不从 \(u\) 走的更短的路,这样从 \(u\) 走的和从另一条路的路径长度不会相等;

\(w_{u,v} < d_v - d_u\),不可能,因为最短路三角形法则,\(d_u+w\ge d_v\),所以说我们要让 \(w_{u,v} = d_v - d_u\)

然后因为题目告诉你了每条边赋权必须是 \(1\sim 9\) 之间的,所以就有 \(1\leq d_v - d_u \leq 9\),转成两个约束条件后跑差分约束即可。那些不在 \(1\)\(n\) 的任何路径上的边我们可以跑一遍 dfs 处理出来,然后随便赋权就行了。

code

加深对差分约束的理解。

首先我们根据题目的关系 \((A,B)\) 我们可以建成 \((A,B,1),(B,A,-1)\) 两种边,然后根据 \((C,D)\) 建出 \((D,C,0)\) 这样的边。然后我们在差分约束系统上先跑一遍看看有没有解。

如果有解,我们继续考虑差分约束能干什么,我们发现差分约束仅能解决两个变量的差值最大/最小之类的问题,统计个数不好做,then how?我们考虑一下我们连的边的情况,\((A,B,1)\)\((B,A,-1)\) 相当于是双向边,而 \((D,C,0)\) 是一种单向边,你会发现在同一个 SCC 里头的点的取值范围是有上下界的,这个待会再看;并且你发现,不在一个 SCC 里头的点是互不影响的,因为连接它们的桥一定是形如 \((D,C,0)\) 这样的边,而这样的边的意义是 \(C\leq D\),我们只需要把这两个 SCC 赋值无限拉远,就能保证它们的取值个数是互不影响的。

再来看同一个 SCC 里的答案是怎么统计的,不妨设 \(t_u\) 这个点取到了下界,\(t_v\) 这个点取到了上界,那么根据最短路三角形最短路法则 \(t_u + d_{u,v} \ge t_v\),其中 \(d_{u,v}\) 表示 \(u\)\(v\) 的最短路,那么我们考虑,这个图中只有边权为 \(1,0,-1\) 的三种边,以为已经取到上下界,所以 \(t_u + d_{u,v}\) 只能 \(=t_v\),因为正边权的权值均为 \(1\),所以这条路径上的点的取值一定经过了 \(t_u\sim t_v\)\(d_{u,v}+1\) 种取值。

那么这也就是说,一个 SCC 中的最大可能取值就是 SCC 中每两个点的最短路的最大值,这也就是每个 SCC 能取到的最大可能取值,但是别忘了 \(+1\),然后以为这个图是稠密图而且 \(n\leq 600\),可以直接用 tarjan 缩点,Floyd 跑差分约束。

code

训训贪心。

发现每次额外的路程都是把某个牛送到某个终点,然后让它等会,出租车折返回去接另一个牛,然后再到一个终点。也就是我们最小化 \(\sum |s_i-t_j|\) 即可,这个可以排序然后同位置相减。具体证明?交错和 \(\leq\) 包含和。然后把 \(0\) 设为一个终点,\(m\) 设为一个起点,因为要加上从 \(0\) 到第一个起点的路程,终点同理。

但是还是感觉很迷惑啊,很人类智慧。

贪心题遇事不决就排序,关键是怎么排,这tm感觉纯凭感觉啊。

感性理解一下,我们要优先选择一些适用范围比较小的防晒霜,这样才能让适用范围大的更多的去覆盖。具体来说,我们按照 minSPF 递减的顺序把奶牛排序,依次考虑每头奶牛。

对于每头奶牛,扫描一遍所有的防晒霜,在这头奶牛能用的且有剩余的防晒霜里,SPF 值最大的使用,妈的太人类智慧了。

因为奶牛已经按照 minSPF 递减排序,所以对于每一个不低于当前奶牛 minSPF 的防晒霜,都不会低于后面其他奶牛的 minSPF,也就是说,对于当前奶牛可以用的两瓶防晒说 \(x,y\),如果 \(SPF_x < SPF_y\),那么后面其他奶牛只可能出现 “\(x,y\) 都能用”,“\(x,y\) 都不能用”和“\(x\) 可用,\(y\) 不可用”这三种情况之一。因此,当前奶牛选择 maxSPF 较大的 \(y\) 去使用,可以贪心地把 \(x\) 留给后面。

另外,每头奶牛对答案的贡献至多是 \(1\)。即使让它放弃日光浴,留下防晒霜给后面的用,对答案的贡献也不会变的更大。综上所述,这个构造方法真几把牛逼。

被诈骗了。

我们考虑一个 \(4\times 4\) 的矩阵,它是由 \(4\)\(2\times 2\) 的矩阵组成的,因为要满足题目的性质,所以 \(4\) 个小矩阵都要是奇数个 \(1\),那么这个大矩阵一定是 奇数 \(+\) 奇数 \(+\) 奇数 \(+\) 奇数 \(=\) 偶数,又因为 \(n \leq m\),所以 \(n \ge 4\) 一定无解!!!

那么这个题的规模就减少了巨大多。剩下的分类讨论一下就行了。如果 \(n=1\),答案一定为 \(0\);如果 \(n=2\),考虑每列,对于全局一定是偶奇偶奇或者奇偶奇偶这么分布下去,我们给个 \(ans_1,ans_2\) 分别统计到达这两种状态需要的最少操作次数即可,并且没列转化奇偶性最多花费 \(1\);如果 \(n=3\)\(1,2\) 行和 \(2,3\) 行就可以分为上下均为偶奇.../上下均为奇偶.../上偶奇下奇偶/上奇偶下偶奇 \(4\) 种情况,手玩一下可以发现对于每一列,最多只需要 \(1\) 次操作就能从一个不对的状态转移到我们想要的状态,统计一下即可。

posted @ 2023-05-25 20:44  Bloodstalk  阅读(20)  评论(0编辑  收藏  举报