当前位置: 首页 > news >正文

暑假

P2569 https://www.luogu.com.cn/problem/P2569

参考这篇。

/*单调队列优化dp买入股票的转移方程j是顺序枚举的,因为是买入股票,手中的股票应该是越来越多的,
当前的决策有可能在后面(j更大)的时候用到,所以你需要先求出来,
同理,卖出股票时,你手中的股票是越来越少的,
即当前的决策有可能需要用到比你手中股票数更少的状态,
所以需要提前处理,因此需要倒序枚举*/

https://www.luogu.com.cn/record/223728748

AT_arc098_b $x \oplus y \oplus \cdots \oplus z \le x+y+..+z$。异或是不进位的加法,所以有一部分不等全部也不会相等。

P2882 怎么 $O(1)$ 处理 $01$ 数列的区间翻转和实时查询?下面 $a_i \oplus x$ 即为实际值,$b$ 为差分数组,$k$ 为翻转长度,那个 if 只是条件判断。

bool x=0;
for(int i=1;i<=n;i++)b[i]=0;
for(int i=1;i<=n;i++){x^=b[i];if(a[i]^x){x^=1;b[i+k]^=1;}
}

以下三个都相等。

  • 原图中两个点之间的所有简单路径上最大边权的最小值。
  • 最小生成树上两个点之间的简单路径上的最大值。
  • Kruskal 重构树上两点之间的 LCA 的权值。

P6835 https://www.luogu.com.cn/article/icz18zjz 建议再次手推式子。

最长公共子序列转移方程。
$$f_{i,j}=\max(f_{i-1,j},f_{i,j-1})$$
$$if(a_i==b_j)\quad f_{i,j}=\max(f_{i,j},f_{i-1,j-1}+1)$$

P2516 https://www.luogu.com.cn/record/224495353

有三个难点。首先由题可知。

  1. $a_i$ 和 $b_j$ 都属于所有的 $lcs(i,j)$。
  2. $a_i$ 属于所有的 $lcs(i,j)$,$b_j$ 并非属于所有的 $lcs(i,j)$。
  3. $a_i$ 并非属于所有的 $lcs(i,j)$,$b_j$ 属于所有的 $lcs(i,j)$。
  4. $a_i$ 和 $b_j$ 都并非属于所有的 $lcs(i,j)$。

  1. 若$a_i=b_j$,则$f_{i,j}=f_{i-1,j-1}+1$,对应情况 $1$。
  2. 若$f_{i,j}=f_{i,j-1}$,则$b_j$并非属于所有的 $lcs(i,j)$,对应的情况包括 $2,4$。
  3. 若$f_{i,j}=f_{i-1,j}$,则$a_i$并非属于所有的 $lcs(i,j)$,对应的情况包括 $3,4$。
  4. 若$f_{i,j}=f_{i-1,j-1}$,则对应情况 $4$,并且此时也必满足其上两种的条件。

  • 为什么减掉那块?根据容斥原理算重一次,所以减掉。
  • 滚动优化(容易写错)。因为转移最小从前一个开始所以第一维大小 $2$ 就够了。拿新变量滚动第一维。
  • 初始化。第一维滚动了就不用初始化它了。其它边界记得设置最少一种方案不然没法累加。

Skip1

P2051 https://www.luogu.com.cn/problem/P2051
这种格子类的通通考虑状压。每行列最多两个炮。然后分讨这行用了 $0,1,2$ 个炮即可。一道大的分类讨论而已。但是状态设计也是难点。

$f_{i,j,k}$ 表示前 $i$ 行有 $j$ 列放了 $1$ 个棋子,$k$ 列放了 $2$ 个棋子的方案数。

易错点是放 $2$ 棋子。空白加一列的情况。因为这个时候少一列 $1$ 又多一列 $1$。最后列数不变!

凸函数加一次函数还是凸函数。

P3605 子树差分 https://www.luogu.com.cn/problem/P3605

给定一棵 $n$ 个点的树,每个点有点权,求每个点子树内点权大于自身的点的数量,$n \le 10^5$。

大于某个数值的点的数量显然可以通过树状数组维护,关键是怎么精准地计算到单棵子树内。

如果在 DFS 过程中更新树状数组,这样维护的是 DFS 过程中到目前这个点为止各个数值出现的次数,不一定是单棵子树。

考虑 DFS 的过程中一个点会进出(递归与回溯)各一次,实际上进入这个点时,该点的子树信息还没更新到树状数组中,而出这个点时,该点的子树信息已经都加入到树状数组中,因此这两个时间点查询的差值就是该子树的贡献。这种思想被称为子树差分。

子树差分例题:https://www.luogu.com.cn/problem/P1600

popcount(a^b)=popcount(a|b)-popcount(a&b)=
popcount(a)+popcount(b)-2popcount(a&b)

https://www.luogu.com.cn/problem/P3051
https://www.luogu.com.cn/article/sb4oa22b

很应该知道的 trick。

拆环为链拆环为链!

经典题:一条链,给出若干线段,问:若要使线段完整覆盖这条链,最少选几条线段?

将所有候选线段按左端点 $l_i$ 从小到大排序。如果左端点相同,则按右端点 $r_i$ 从大到小排序。

假如当前选择的区间已经覆盖了前 $x$ 个点,为了覆盖第 $x+1$ 个点,一定要再选择一个左端点小于等于 $x+1$ 的区间。那么我们加入一个左端点小于等于 $x+1$,且右端点位置最大的区间,这样能尽可能覆盖最多的点,一定是最优的。

此时把 $x$ 更新为这个右端点的位置。初始时令 $x=0$,不断重复这个过程选择加入的区间,直到 $x=n$ 时终止循环。若某一次选择区间后 $x$ 仍不变,说明第 $x+1$ 个点不可能被覆盖,无解。

对每个 $1 \le i \le n$ 预处理出左端点小于等于 $i$ 的所有区间中最大的右端点位置,就可以用 $O(n+k)$ 的时间预处理,$O(n)$ 选择区间,总时间复杂度 $O(n+k)$ 解决这个问题。

环的话 P6902 就拆,但是还要加一个倍增优化成 $O(n \log n$)。https://www.luogu.com.cn/problem/P6902

https://www.luogu.com.cn/record/225195227

数组越界风险‌:在 for(int j=0;j<=vec[i].size()-1;j++)循环中,如果vec[i]为空,vec[i].size()-1会变成无符号整型的最大值(因为size()返回无符号数),导致循环条件异常,建议改为for(int j=0;j<=(int)vec[i].size()-1;j++)或使用迭代器。

Skip2

喵喵题 P2519 https://www.luogu.com.cn/problem/P2519
https://www.luogu.com.cn/article/jczkipeh

但是重合区间数如果超过区间长度就一定有假话。

这个很好理解,假如区间长度为 $k$,那么就是说有k个人分数相等,但却有多于 $k$ 个人说自己在这段区间内,这显然有人说假话。

蓝题好多喵喵题,喵喵题都是豪题。

https://www.luogu.com.cn/problem/AT_dp_t
https://www.luogu.com.cn/problem/P2467
https://www.luogu.com.cn/article/o0oht8bj
https://www.luogu.com.cn/article/sg1fwhh5

转移方程都是类似的,用来处理波形。
设 $f_{i,j}$ 表示已经考虑了前 $i$ 个位置,且第 $i$ 位上,填的数字是第 $j$ 小的总方案数。(由于是排列所以第 $j$ 小等价于这位填的是 $j$)。

P1070 我终于会正解了!!!

https://www.luogu.com.cn/article/5p3x7ruk

神仙处理:先是要想到固定机器人转工厂和金币。其次是聪明的转移变形。

P6647 https://www.luogu.com.cn/record/225529688

P3188 很有意思的 dp。

连通图最长路求法:拓扑排序加 dp。

  1. 拓扑排序确定节点处理顺序。
  2. 定义 $dp_i$ 表示以节点 $i$ 结尾的最长路径长度。
  3. 递推:$dp_i=max(dp_j+w_{j,i})$,对所有入边 $j\rightarrow i$。

Skip 3

P1600 https://www.luogu.com.cn/problem/P1600 。

题解:https://www.luogu.com.cn/article/nkwnx1ti 。

分讨两段路 $s \rightarrow lca$ 和 $lca \rightarrow t$,推一下能被观察到的条件,用子树差分做即可。

https://www.luogu.com.cn/record/226182410

子树结点的 DFS 序构成一段连续区间,可以直接遍历。

圆方树的点数小于 $2n$,这是因为割点的数量小于 $n$,所以请注意各种数组大小要开两倍。

欧拉路径伪代码。

void dfs(int now)
{for(int i=del[now];i<G[i].size();i=del[now]){del[now]=i+1;dfs(G[now][i])}st.push(now);}//其中 del[now] 表示 G[now][1,2……,del[now]-1] 
//都已被标记,下一次要从G[now][del[now]]开始访问。

‌SPFA 算法允许一条边被多次经过,Dijkstra算法不允许一条边被多次经过。

CF609E:$n$ 个点,$m$ 条边,如果对于一个最小生成树中要求必须包括第 $i(1 \le i \le m)$ 条边,那么最小生成树的权值总和最小是多少。$1 \le n \le 2 \times 10^5$,$n-1 \le m\le 2 \times 10^5$。

求出最小生成树,添加第 $i$ 条边,之后出现一个环。把环上权值最大的边断掉就可以了。当然不能断自己,所以断的是原 mst 上 $u\rightarrow v$ 权值最大的边。做个树剖维护一下就行了。

同余最短路。

https://www.luogu.com.cn/problem/P3403 。

https://www.luogu.com.cn/problem/P2371 。

多点最短路的最小值化两点最短路。

https://www.luogu.com.cn/problem/P5304 。

分为两个集合,确保集合之间的点一定不构成最小的最短路,之后用起点 $s$ 连点集 $1$(边权为 $0$),终点 $t$ 连点集 $2$,点集 $1,2$ 相连。那么原问题变成了 $s\rightarrow t$ 的最短路。

重点是怎么划分点集。https://www.luogu.com.cn/problem/solution/P5304?page=1 。

https://www.luogu.com.cn/problem/P3199 。

令 $ans$ 为带权有向图所有环的平均值的最小值。对于任意有向图 $G$,添加一点 $s$ 后。

$$ans=\min_{v \in V,F_n(v)\ne \infty }\max_{0 \le k \le n-1}[\frac{F_n(v)-F_k(v)}{n-k} ]$$

其中 $F_k(v)$ 是 $s \rightarrow v$ 恰好走了 $k$ 条边的最短路(不存在则为 $\infty$)。

$01$ 分数规划:https://blog.csdn.net/niiick/article/details/80925267 。

Skip 4

https://www.luogu.com.cn/problem/P3430 。

给定两个数组,要求每个数组中不可以出现相同的数字。两个数组同一位置的数可以互换,最少换几次满足上述要求?


如果两个相同的数在不在同一行,给两个数所在的列的编号连一条边权为 $0$ 的边,否则连一条边权为 $1$的边。

给每个联通块的点黑白染色($1$ 边连接的点颜色相反,$0$ 边连接的点颜色相同),答案每次加上每个联通块黑点和白点个数的最小值。

https://www.luogu.com.cn/article/rfudqj2s 。

题解有一点不清楚,这里感谢群内的 VainSylphid 大佬解答。以下是原话:

你发现 $1$ 边相当于,我们希望这两列中恰有一列做交换。$0$ 边相当于,如果这两列中一列交换了,另一列也要交换。

那你按照那个题解的方式染色,就相当于同一个颜色的必须全部交换或者全部不交换。

并且黑色和白色只有一种颜色全部交换,另一种全部不交换。

那取较小值就是显然的了。

2025.7.28-7.29 斜率优化专题

今天是斜率优化专题。https://www.bilibili.com/video/BV1dKSJY5Ew5 。

P2365 https://www.luogu.com.cn/article/367gcax3

费用提前计算,牛的。只需要把 $s$ 对 $j+1$ 后的影响补充到费用中就可以了。新段的费用已经叠进 $f$ 里了。

https://www.luogu.com.cn/record/227393110

移项要遵循的原则是:

  • 把含有 $function(i)\times function(j)$ 的表达式看作 $kx$。
  • 含有 $dp_i$ 的项必须要在 $b$ 的表达式中。
  • 含有 $function(j)$ 的项必须在 $y$ 的表达式中。
  • 如果未知数 $x$ 的表达式单调递减,最好让等式两边同乘个 $−1$,使其变为单增。

精简记忆版本:

  • 交叉项 $kx$。
  • $dp_i \rightarrow b$。
  • $fuc_j \rightarrow y$。

用单调队列维护凸包点集,操作分三步走:

  1. 进行择优筛选时,在凸包上找到最优决策点 $j$。
  2. 用最优决策点 $j$ 更新 $dp_i$ 。
  3. 将 $i$ 作为一个决策点加入图形并更新凸包(如果点 $i$ 也是 $dp_i$ 的决策点之一,则需要将 3 换到最前面)。

Skip 5

https://www.luogu.com.cn/problem/P5677 。

好题喵。

:::info
排序,处理所有好对。离线,左端点从小到大排序,有小于等于当前枚举 $r$ 的线就不断加进来,再去掉 $l$ 之前的线就是当前贡献。
:::

https://www.luogu.com.cn/record/228032569 。

P7706 https://www.luogu.com.cn/problem/P7706 。

不会啊,思考 10 分钟无果,遂怒。厉害的题目,其实很板,只是我不会这个 trick。https://www.luogu.com.cn/article/oj2vo7sx 。讲的很清楚无需赘述了。

https://www.luogu.com.cn/problem/P3792
https://www.luogu.com.cn/problem/P5278

“双倍经验”,https://www.luogu.com.cn/article/itudyc23 学习思路。

在洛谷 P5278 题中,维护前驱的最大值(即区间内每个元素上一次出现位置的最大值)的核心目的是快速检测元素重复性。若该最大值大于等于区间左端点,说明至少有一个元素在区间内重复出现。

置换环 https://www.cnblogs.com/TTS-TTS/p/17047104.html 。

  • 欧拉序 LCA 算法

‌预处理复杂度‌:$O(n + n \log n) = O(n\log n)$。

查询复杂度‌:$O(1)$。

‌实现方式‌:将 LCA 问题转化为 RMQ 问题,使用 ST 表解决。

  • 倍增 LCA 算法

‌预处理复杂度‌:$O(n\log n)$。

‌查询复杂度‌:$O(\log n) $。

‌实现方式‌:预处理每个节点的 $2^k$ 级祖先,使用倍增思想进行查询。

虚树中,可以发现每个蓝点对应一个连通块,且连通块大小为该蓝点对应原树中子树大小减去蓝点儿子子树大小。

Skip 6

假设 $G=(X,Y,E)$ 是二分图,且 $\left | X \right | \le \left | Y \right |$。对于任意 $W\subseteq X$,记 $V(W)$ 为 $G$ 中所有与 $W$ 的顶点相邻的顶点集合。那么当且仅当 $\left | W \right | \le \left | V(W) \right |$ 对于所有 $W\subseteq X$ 都成立, 存在对于图 $G$ 的一个匹配 $M$ 使得 $X$ 中的所有点都是匹配点

所有正则的二分图都有完美匹配。(正则二分图中,所有顶点的度数都相同)。

删除操作可以离线倒过来变成加入操作。


有一个 $n\times m$ 的矩阵 $A$。$A$ 中每个元素都是 $[0,10^5]$ 中的整数。且形式化地,有

$$
E_{i,j}=\sum_{1\le x\le n}\sum_{1\le y\le m}(|x-i|+|y-j|)A_{x,y}
$$

现在要用矩阵 $E$ 还原出一个符合上述条件的整数矩阵 $A$。不过还原出的 $A$ 中的每个元素均属于 $[0,10^{16}]$ 即可。

要求单次时间复杂度为 $O(nm)$。


怎么这么牛的一道题。

设:

$$C_i=\sum_{j=1}^{m}A_{i,j}$$

$$
E_{i,j}=\sum_{1\le x\le n}\sum_{1\le y\le m}(|x-i|+|y-j|)A_{x,y}
$$

由这个式子,可以注意到:

$$E_{i,j}=\sum_{x=1}^{n}\left |x-i \right |\times C_x+\sum_{x=1}^{m}\left |x-j \right |\times R_x$$

进一步的有:

$$E_{i-1,j}+E_{i+1,j}-2\times E_{i,j}=2\times C_i$$

其中 $2 \le i \le n-1$。

同理可以解出 $R_j$。

$$R_j=\sum_{i=1}^{n}A_{i,j}$$

也就是说,确定不了的只剩下 $C_1,C_n,R_1,R_m$。

考虑贪心,我们依次考虑每个 $A_{i,j}$,直接将其为 $\min(R_i,C_i)$,然后把 $R_i$ 和 $C_i$ 同时减去 $A_{i,j}$,一直接着做下去。那么只要有解,这样做就一定能构造出一个 $A_{i,j}$ 为非负整数的解。

于是这道神仙题就做完了(吗?)。

:::info
注意这里需要特判 $n=1$ 和 $m=1$ 的情况!这两个需要特判换一个解法!
:::

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define For(i,l,r) for(int i=l;i<=r;i++)
int n,m;
void solve(){cin>>n>>m;vector<vector<int> >E(n+1),A(n+1);vector<int>C(n+1),R(m+1);For(i,1,n){E[i].resize(m+1);A[i].resize(m+1);}For(i,1,n)For(j,1,m)cin>>E[i][j];For(i,2,n-1)C[i]=(E[i-1][1]+E[i+1][1]-2*E[i][1])/2;For(i,2,m-1)R[i]=(E[1][i-1]+E[1][i+1]-2*E[1][i])/2;if(n==1&&m==1){cout<<E[1][1]<<"\n";return ;}if(n==1){R[1]=E[1][m];For(i,2,m-1)R[1]-=(m-i)*R[i];R[m]=E[1][1];For(i,2,m-1)R[m]-=(i-1)*R[i];R[1]/=m-1,R[m]/=m-1;For(i,1,m)cout<<R[i]<<" ";cout<<"\n";return;}if(m==1){C[1]=E[n][1];For(i,2,n-1)C[1]-=(n-i)*C[i];C[n]=E[1][1];For(i,2,n-1)C[n]-=(i-1)*C[i];C[1]/=n-1,C[n]/=n-1;For(i,1,n)cout<<C[i]<<"\n";return;}int X=E[1][1];int Y=E[1][m];int Z=E[n][1];For(i,2,n-1){X-=(i-1)*C[i];Y-=(i-1)*C[i];Z-=(n-i)*C[i];}For(i,2,m-1){X-=(i-1)*R[i];Y-=(m-i)*R[i];Z-=(i-1)*R[i];}int W=0;For(i,2,m-1)W+=R[i];For(i,2,n-1)W-=C[i];W=W*(n-1)*(m-1);W-=(m-1)*Z-(n+m-2)*X-(n-1)*Y;W/=n-1,W/=2*n+2*m-4;C[n]=W;C[1]=(Z-X)/(n-1)+C[n];R[1]=(Y-(n-1)*C[n])/(m-1);R[m]=(X-(n-1)*C[n])/(m-1);For(i,1,n){For(j,1,m){A[i][j]=min(C[i],R[j]);C[i]-=A[i][j];R[j]-=A[i][j];}}For(i,1,n){For(j,1,m){cout<<A[i][j]<<" ";}cout<<"\n";	}
} 
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int T;cin>>T;while(T--)solve();return 0;
}

Skip 7

两个栈,每个栈中均有 $n\times K$ 个元素,其中数字 $1,2,\cdots, n$ 各分别出现了恰好 $K$ 次。

不断进行如下操作,直到有一个栈变为空:

  • 如果当前两个栈的栈顶元素相同,则将两个栈的栈顶元素都弹出,获得 $1$ 的得分。
  • 如果当前两个栈的栈顶元素不同,则选择一个栈的栈顶元素弹出,不得分。

计算通过合理的操作,最大得分是多少。

对所有数据,保证 $1\le n\le 10^4,1\le K\le 16$。


考虑所有得分的时刻,把配对上的两个元素在原栈中的位置标出来,那么得到两个单调递增序列 $x,y$。其中满足 $a_{x_i}=b_{y_i}$。

反之,如果两个序列满足上面的要求,那么就可以通过⼀定的操作得到分数。于是目标就变成了在满足条件下最大化序列⻓度。

把所有可能的 $(x_i,y_i)$ 拿出来,定义全序关系 $i < j$ 时有 $(x_i,y_i)<(x_j,y_j)$。(这里的小于是两维度都小于)。共 $nK^2$ 对。

:::info
为什么是 $nK^2$ 对?因为共 $n$ 个数字,每个数字在第一个栈里出现 $K$ 次,也在第二个里出现 $K$ 次。
:::

找最长上升子序列即可。别被诈骗了。

#include<bits/stdc++.h>
using namespace std;
#define For(i,l,r) for(int i=l;i<=r;i++)
const int N=2e5+10;
int n,k,a[N],ans;
int t[N],p[N][20],cnt[N];
inline int qry(int x){int res=0;for(;x;x-=x&(-x))res=max(res,t[x]);return res;
}
inline void upd(int x,int v){for(;x<=n*k;x+=x&(-x))t[x]=max(t[x],v);
}
int main(){cin>>n>>k;For(i,1,n*k)cin>>a[i];For(i,1,n*k){int x;cin>>x;p[x][++cnt[x]]=i;}For(i,1,n*k){for(int j=k;j>=1;j--){int res=qry(p[a[i]][j]-1)+1;ans=max(ans,res);upd(p[a[i]][j],res);}}cout<<ans;return 0;
}

Skip 8

判断 $k$ 个数中 $1$ 至 $k$ 每个值正好各出现一次

把 $1$ 至 $k$ 赋值成随机 $64$ 位无符号整数 $p_i$ 。
预处理 $s_i$ 对于每个 $i (1\le i\le k)$ 的 $1$ 至 $i$ 异或和。
判断 $k$ 个数的权值异或和是否等于 $s_k$。

$\max(p,q)-\min(p,q)=\left |p-q \right |$。

求 $mex=i \rightarrow$ 求 $mex \geq i$。

不重不漏找子集:for(int i=u;i;i=(i-1)&u)f[u]+=f[i];//i是u的子集

对于树上任何一个结点 $u$,他到根的路径上重链数不会超过轻边数 $-1$。

对于树上任何一个结点 $u$,他到根的路径上的重链条数不会超过 $O(\log n)$,轻边条数不会超过 $O(\log n)$。

Skip 9

长为 $n$ 的序列 $a$ 满足$0\le a_i < 2^m$ 且序列中的数字两两不同。约定 $F(x)$ 为 $\forall a_i, a_i\rightarrow a_i \oplus x$ 后序列逆序对的个数。

现在需要找到第 $k$ 小的 $x$。称 $x_i<x_j$,当且仅当 $F(x_i)<F(x_j)$,或者 $F(x_i)=F(x_j)$ 且 $x_i<x_j$。

对于 $100%$ 的数据,$1\le T\le 10$,$1\le n,\sum n\le 10^6$,$1\le m\le 30$,$0\le a_i < 2^{m}$,$1\le k\le 2^m$。保证 $a_i$ 互不相同。

异或后比大小可以拆位考虑一下。令两数最高不同的二进制位为 $L$,则两数大小关系改变当且仅当 $x$ 的第 $L$ 位为 $1$。

:::info
这是因为前 $L-1$ 位相同,异或同一个数字没影响,因为第 $L$ 位不管 $x$ 数字是什么,两个数这一位必定不相同,所以后 $n-L$ 位的值对大小没影响。

那么易得出 $x$ 这一位为 $1$ 可以改变大小关系。

$0 \oplus 1=1,1\oplus 1=0;0 \oplus 0=0,1\oplus 0=1$
:::

因此每个二进制位是独立的。考虑求出初始逆序对个数,以及每个二进制位异或上 $1$ 时逆序对的改变量。
前者可以通过归并排序或树状数组轻松求出。

:::info
逆序对的改变量指的是:把前面二进制位相同的放一组,每组求逆序对。
:::

对于后者,从高到低枚举二进制位,对这一位不同的数求逆序对(只有 $0/1$),然后根据这一位将序列分为两个子序列,对两个序列分别往下做。

那么这部分时间复杂度为 $O(nm)$。

那么接下来问题就变为:有 $m$ 个数,从中选出若干个数并求和,要求找到第 $k$ 小的和。先把 $m$ 个数分成两份,每份 $\frac{m}{2}$ 个数,然后对每份求出所有选法的和,并排序。时间复杂度 $O(m2^{\frac{m}{2}})$。

这样就变成从两个序列中各选一个进行组合。然后二分答案 $s$,二分时计算出有多少种选法的和小于等于 $s$。这里可以用双指针在两个序列上扫。这部分时间复杂度为 $O(2^{\frac{m}{2}}\log n)$。

Skip 10

遇到删除就时光倒流做添加。

“两点 $u,v$ 存在两条边不相交路径”与“$u,v$ 属于同一个边双”是互为充要条件的。

:::info
Menger 定理:在一个无向图中,两个不同顶点 $u$ 和 $v$ 之间的最大/边不相交/路径数,等于将 $u$ 和 $v$ 断开所需删除的最少边数。
:::

题目。

简化版题目为 LeetCode 253,以下是简化版本的解法。

每支乐队的演唱会时间不能相交,等价于“为每个区间分配一个颜色,且相交的区间颜色不同”。我们需要最少的颜色数,这就是区间图的最小着色数。

对于任意区间集合,最小着色数 $=$ 区间集合在任意时刻的最大重叠次数

步骤如下。备注:不管区间有无无包含关系,这样的算法都是正确的。因为考虑堆内的元素如果现在能用之后也能用,现在用掉一定不劣。而且不会因为排序对后面产生不同影响,因为因为左端点相同的都放一块了,扫到下一个的时候早就已经全部入队了。

  • 首先,对区间按左端点升序排序。

  • 然后,用小根堆优先队列,计算最大重叠次数。

    • 对于当前区间 $[l_i, r_i]$,先检查堆顶元素(最早结束的演唱会结束时间 $top_r$)。
    • 若 $top_r < l_i$,即最早结束的演唱会在当前演唱会开始前已结束,无重叠。则弹出堆顶元素。该乐队可承接当前演唱会,无需新增乐队。
    • 将当前区间的右端点 $r_i$ 加入堆,表示新增一场正在进行的演唱会。若堆大小增加,则可能需要更多乐队。
    • 堆的大小即当前同时进行的演唱会数量,也就是此时需要的乐队数,取最大值即为答案。

说回本题。

考虑确定时间段区间怎么求乐队数量。从左往右依次贪是对的,现在能继承⼀定是继承更优,记录⼀下现在还在堆里的右端点,如果最左边的那个 $\le l$,弹出并把 $r$ 加⼊,代价为 $0$。否则直接把 $r$ 加⼊,代价为 $1$。

考虑每个时间段的覆盖次数 $t_i$,根据上述分析答案就是 $\max t_i$。

直接 dp $t_i$,令 $t_0=t_{n+1}=0$。注意到序列 $t$ 合法当且仅当 $\left | t_{i+1}-t_i \right |\le 1$。

还需要考虑 $t_i$ 对应多少个区间集合。$t_i=t_{i+1}$ 考虑被 $i$ 覆盖的每个区间,要么不变;要么左端点最左边的那个在 $i$ 结束,同时在 $i+1$ 新开一个区间。一共 $2$ 种方案。

记 $k=\sum_i[t_i \ne 0 \ \land \ t_i=t_{i+1}]$,那么贡献就是 $2^k$。这⾥的⽅案对其他位置的影响是独⽴的,因为只需要考虑相邻位置的映射,所以贡献是 $2$。其余情况映射都是唯⼀的。

因为合法的区间集合与满足 $|t_{i+1}-t_i| \leq 1$ 的覆盖次数序列 $t$ 是一一对应关系,且序列中相邻位置 $t_i$ 与 $t_{i+1}$ 的关系直接决定了区间在 $i$ 到 $i+1$ 处的增减(对应区间开始或结束),仅需通过相邻位置的 $t$ 就能唯一确定区间集合的结构,进而计算其贡献,无需考虑非相邻位置。

暴力 dp 即可,是 $O(n^3)$ 的。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define For(i,l,r) for(int i=l;i<=r;i++)
const int p=998244353;
int n;
/*动态规划计算当最多使用
k支乐队时的合法集合数量
冲突的定义:
两个演唱会的时间区间存在重叠
包括端点处重叠*/ 
ll dp(int k){/*f[i]表示考虑完所有时间段后,被选中的演唱会中最大冲突深度为i的合法集合数量f[i]统计范围是所有选中的演唱会彼此完全不相交的集合例如 [1,2]、[3,4]、[5,6] 这样的时间段,任意两个都不重叠,同时进行的演唱会最多只有 1 个*/vector<ll>f(n+1),g(n+1);f[0]=1;For(i,0,n-1){fill(g.begin(),g.end(),0);For(j,0,k){/*情况1:不选当前时间段 或选了但不增加冲突深度(j=0时无冲突*/g[j]=(g[j]+f[j]*(j?2:1))%p;//情况2:从深度j+1转移到jif(j+1<=k){g[j]=(g[j]+f[j+1])%p;}//情况3:从深度j-1转移到jif(j-1>=0){g[j]=(g[j]+f[j-1])%p;}}swap(f,g);//新状态变成旧状态,旧状态直接等覆盖 }//返回最多使用k支乐队的合法集合总数return (f[0]+f[1])%p;
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n;For(k,1,n)cout<<dp(k)<<" ";return 0;
}

tarjan 算法告诉我们:如果 $y$ 是 $x$ 的子节点且 $low(y)≥dfn(x)$,那么 $x$ 就是割点。若根 $x$ 有两颗及以上的子树,那么 $x$ 即为割点。

差分约束求最长路,得到的解在有下界约束的情况下,字典序最小。

若一条路径的 lca 在另一条路径上,那么这两条路径有交,否则无交。

http://www.wxhsa.cn/company.asp?id=3267

相关文章:

  • 做题记录
  • 课程助教工作总结
  • 6G 驱动的智慧城市新格局
  • SHA-1 证书淘汰警告:网站管理员需紧急验证TLS安全性
  • 数字孪生在制造业中的应用
  • device第一周个人作业
  • Java 在移动开发与跨平台应用中的应用
  • 5G 技术与远程教育
  • 5G 技术在工业互联网的应用
  • 一键部署ftp脚本
  • PySimpleGUI安装4.60.5老版本安装教程!
  • PySimpleGUI-免注册版本
  • 高三闲话 #1
  • 三大免费CDN推荐:安全防护强、稳定性卓越、加载飞速,长期使用超安心
  • PySimpleGUI 开始注册了,怎样能免注册使用早期版本?
  • 全屏与退出全屏功能
  • 二十多年.NET老兵重返技术博客
  • 5月杂题
  • uv,下一代Python包管理工具
  • 阅读 |《虚空》观后感以及一些想法——万物简史
  • Python进阶必看:深入解析yield的强大功能
  • polyglot,一个有趣的 Python 库!
  • 个人介绍与博客初建
  • PySimpleGUI,一个神奇的 Python 库!
  • c++ 的拷贝构造函数
  • 变异
  • 【笔记】类欧几里得算法
  • AQS的一些思考
  • DearPyGui-最强大的一款Python GUI工具
  • 2 模型评估与选择