整理的算法模板合集: ACM模板

点我看算法全家桶系列!!!

实际上是一个全新的精炼模板整合计划


题目链接

https://hydro.ac/d/bzoj/p/2138

是 hydro 的 BZOJ 修复工程 !(我也去领了一点题慢慢修着玩,这题就是我修的嘿嘿嘿)

题目描述

话说 Nan 在海边等人,预计还要等上 MMM 分钟。为了打发时间,他玩起了石子。 Nan 搬来了 NNN堆石子,编号为 1∼N1\sim N1∼N,每堆包含 AiA_iAi​ 颗石子。每 111 分钟,Nan 会在编号在 [Li,Ri][L_i,R_i][Li​,Ri​] 之间的石堆中挑出任意 KiK_iKi​ 颗扔向大海(好疼的玩法),如果 [Li,Ri][L_i,R_i][Li​,Ri​] 剩下石子不够 KiK_iKi​ 颗,则取尽量地多。为了保留扔石子的新鲜感,Nan 保证任意两个区间 [Li,Ri][L_i,R_i][Li​,Ri​] 和 [Lj,Rj][L_j,R_j][Lj​,Rj​] ,不会存在 Li≤LjandRj≤RiL_i\le L_j\ \mathrm{and}\ R_j\le R_iLi​≤Lj​ and Rj​≤Ri​ 的情况,即任意两段区间不存在包含关系。可是,如果选择不当,可能无法扔出最多的石子,这时 Nan 就会不高兴了。所以他希望制定一个计划,他告诉你他 mmm 分钟打算扔的区间 [Li,Ri][L_i,R_i][Li​,Ri​] 以及 KiK_iKi​ 。现在他想你告诉他,在满足前 i−1i-1i−1 分钟都取到你回答的颗数的情况下,第 iii 分钟最多能取多少个石子。

输入格式

第一行正整数 NNN ,表示石子的堆数;

第二行正整数 x,y,z,Px,y,z,Px,y,z,P,1≤x,y,z≤N1\le x,y,z\le N1≤x,y,z≤N,P≤500P\le 500P≤500;

有等式 A[i]=[(i−x)2+(i−y)2+(i−z)2]modPA[i]=[(i-x)^2+(i-y)^2+(i-z)^2] \mod PA[i]=[(i−x)2+(i−y)2+(i−z)2]modP;

第三行正整数 MMM,表示有 MMM 分钟;

第四行正整数 K[1],K[2],x,y,z,PK[1],K[2],x,y,z,PK[1],K[2],x,y,z,P,x,y,z≤1000x,y,z\le 1000x,y,z≤1000,P≤10000P\le 10000P≤10000;

有等式 K[i]=(x×K[i−1]+y×K[i−2]+z)modPK[i]=(x\times K[i-1]+y\times K[i-2]+z)\mod PK[i]=(x×K[i−1]+y×K[i−2]+z)modP。

接下来 MMM 行,每行两个正整数 L[i],R[i]L[i],R[i]L[i],R[i]。

输出格式

有 MMM行,第 iii 行表示第 iii分钟最多能取多少石子。

输入样例

5
3 2 4 7
3
2 5 2 6 4 9
2 4
1 2
3 5

输出样例

2
5
5

数据规模与约定

对于 100%100\%100%​​​​ 的数据,N≤4×104N\le 4\times 10^4N≤4×104,M≤NM\le NM≤N,1≤L[i]≤R[i]≤N1\le L[i]\le R[i]\le N1≤L[i]≤R[i]≤N,A[i]≤500A[i]\le 500A[i]≤500

提示

样例说明:

石子每堆个数分别为 0,5,2,5,00,5,2,5,00,5,2,5,0​​​​​​​​​​。
第 111​ 分钟,从第 222​​​​​​​​​​​ 到第 444​​​​​​​​​​​​ 堆中选 222​​​​​​​​​​​​ 个;
第 222​​ 分钟,从第 111​​​ 到第 222​​​​​​​​​ 堆中选 555​​​​​​​​ 个;
第 333 分钟,从第 333​​​​ 到第 555​​​​​ 堆中选 888​​​​​​​ 个,但最多只能选 555​​​​​​ 个。

Solution

首先我们来复习几个基本图论概念:

二分图完美匹配

如果一个图的某个匹配中,所有的顶点都是匹配点,那么它就是一个完美匹配。

换句话说,对于一个二分图 G<V1,V2,E>,∣V1∣≤∣V2∣G<V_1,V_2,E>,|V_1|\le |V_2|G<V1​,V2​,E>,∣V1​∣≤∣V2​∣,若匹配 MMM 包含 V1V_1V1​ 的所有点,即 ∣M∣=∣V1∣|M|=|V_1|∣M∣=∣V1​∣,则成 MMM 为 V1V_1V1​ 到 V2V_2V2​ 的完美匹配。

霍尔定理(hall 定理)

设二分图的两部分为X、YX、YX、Y,且 ∣X∣≤∣Y∣|X|≤|Y|∣X∣≤∣Y∣。则定理描述为:二分图存在完美匹配,等价于对于 XXX 的任意子集 X′X′X′ ,与子集 X′X'X′ 相连的 YYY 的结点个数≥∣X′∣≥|X′|≥∣X′∣。

另一种表示方式:

对于一个二分图 G(V1,V2,E),(∣V1∣≤∣V2∣)G(V_1,V_2,E),(|V_1|\le|V_2|)G(V1​,V2​,E),(∣V1​∣≤∣V2​∣),对于 ∀X⊆V1\forall X⊆ V_1∀X⊆V1​,定义 N(X)=vj∣(vi,vj)∈E,vj∈V2,vi∈XN(X)=v_j|(v_i,v_j)\in E,v_j\in V_2,v_i\in XN(X)=vj​∣(vi​,vj​)∈E,vj​∈V2​,vi​∈X。(N(X)N(X)N(X) 即 XXX 的邻居结点集)

其存在 V1V_1V1​ 的完美匹配的充要条件为 ∀X⊆V1,∣X∣≤∣N(X)∣\forall X⊆V_1,|X|\le |N(X)|∀X⊆V1​,∣X∣≤∣N(X)∣。

霍尔定理听上去像是一句废话,但是霍尔定理的推论应用更加实用一些。

霍尔定理推论

假设两边的点集分别为 XXX,YYY,则二分图的最大匹配数为 ∣X∣−max⁡{∣W∣−∣N(W)∣}|X|−\max\{|W|−|N(W)|\}∣X∣−max{∣W∣−∣N(W)∣},其中 WWW 是 XXX 的子集。

对于一些特殊的题目,它可以免去建图而直接求最大匹配。

例题:有 nnn 个人,每个人有特定几把椅子可以选择去坐,问最少添加几把椅子可以使得所有的人都有椅子坐。

显然我们需要满足所有的人都有椅子坐,我们可以把人和每个人可选的椅子连边建成一个二分图,求二分图最大匹配即当前的椅子最多能让多少个人坐,总人数减去最大匹配数即为需要增添的椅子数量。但是我们直接求二分图匹配,显然会超时,对于这种二分图最大匹配超时问题,我们就可以使用霍尔定理的推论免去建图直接求解最大匹配。

对于任意一个客人集合 XXX ,对应的椅子的集合 YYY ,若 ∣X∣≤∣Y∣|X|\le |Y|∣X∣≤∣Y∣,则显然具有完美匹配,若 ∣X∣≥∣Y∣|X|\ge |Y|∣X∣≥∣Y∣ ,则 XXX 至少要删去 ∣X∣−∣Y∣|X|−|Y|∣X∣−∣Y∣ 个元素,才能有完美匹配,设 N(x)=∣X∣−∣Y∣N(x)=|X|−|Y|N(x)=∣X∣−∣Y∣,显然根据霍尔定理推论,二分图的最大匹配数为 ∣X∣−max⁡{∣X′∣−∣N(X′)∣},X′⊆X|X|-\max\{|X'|-|N(X')|\},X'⊆X∣X∣−max{∣X′∣−∣N(X′)∣},X′⊆X。


回到本题,我们对每堆石子的询问能否丢掉 kik_iki​ 个石子,实际上就是一次求二分图的最大匹配。

我们可以对于每堆石子,拆成 aia_iai​ 个点作为二分图的左部,询问拆成 kik_iki​ 个点作为二分图的右部,答案显然就是该二分图的最大匹配,但是跑 nnn​ 次网络流求二分图最大匹配复杂度为 O(nmn)O(nm\sqrt n)O(nmn​) 显然超时,考虑优化。

对于超时的二分图匹配问题我们显然考虑 hall 定理。

我们考虑对于每次询问,判断是否满足 hall 定理,若满足,则存在完美匹配,输出 kik_iki​​ 即可。若不满足,输出满足 hall 定理能承受的上限即可。

hall 定理需要对于所有任意子集判断,显然复杂度较高,我们这里每次给定的是若干个区间,我们可以证明,若每个区间均满足 hall 定理,

我们先对输入的数据进行处理,显然所有不在询问区间内的石子堆是没有用处的,删掉即可。

设 kik_iki​ 表示第 iii 次丢石子操作丢掉的石子数,aia_iai​ 表示第 iii 个石子堆的石子数。

根据 hall 定理 ∣X∣<∣Y∣|X|<|Y|∣X∣<∣Y∣​​​,对于,以及给定的询问区间 L[],R[]L[],R[]L[],R[],显然有
∑i=lrki≤∑i=L[l]R[r]\displaystyle \sum_{i=l}^{r}k_i\le \sum_{i=L[l]}^{R[r]} i=l∑r​ki​≤i=L[l]∑R[r]​

显然是一个前缀和结构,预处理出 SA,SKSA,SKSA,SK​​​ 为 a,ka,ka,k​​ 的前缀和

设 VR=SA[R[i]]VR = SA[R[i]]VR=SA[R[i]]​​​​​​​​,VL=SA[L[i+1]−1]VL = SA[L[i+1]-1]VL=SA[L[i+1]−1]​​​​​。

上式即可简化为:
SK[r]−SK[l−1]≤VR[r]−VL[l−1]SK[r]−VR[r]≤SK[l−1]−VL[l−1]\begin{aligned}&SK[r]−SK[l-1]\le VR[r]-VL[l-1]&\\& SK[r]-VR[r]\le SK[l-1]-VL[l-1] \end{aligned} ​SK[r]−SK[l−1]≤VR[r]−VL[l−1]SK[r]−VR[r]≤SK[l−1]−VL[l−1]​
设 f[i]=SK[r]−VR[r]f[i]=SK[r]-VR[r]f[i]=SK[r]−VR[r],g[i]=SK[l−1]−VL[l−1]g[i]=SK[l-1]-VL[l-1]g[i]=SK[l−1]−VL[l−1]

即:f[r]≤g[l−1]f[r]\le g[l-1]f[r]≤g[l−1]

然后考虑如何计算答案。

对于当前的时间 ttt,我们需要输出 ktk_tkt​,显然根据题意我们需要找到最大的 xxx ,使得 kt=xk_t=xkt​=x 输出。

令 kt=xk_t=xkt​=x​​​​ 之后,kkk​ 数组的前缀和 SK[t]∼SK[m]SK[t]\sim SK[m]SK[t]∼SK[m]​​ 均 +x+x+x,f[t]∼f[m]+=xf[t]\sim f[m]+=xf[t]∼f[m]+=x,g[t+1]∼g[m]+=xg[t+1]\sim g[m]+=xg[t+1]∼g[m]+=x

仅 f[t]f[t]f[t] 会减少,此时我们能拿走的石头数量根据题意为 min⁡{ki,f[r]−g[l−1}\min\{k_i,f[r]-g[l-1\}min{ki​,f[r]−g[l−1}​

即 kt=x≤g[j]−f[i]k_t=x\le g[j]-f[i]kt​=x≤g[j]−f[i]。

建立线段树取 fff 的最大值,取 ggg​ 的最小值更新即可。

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;int n, m, s, t;
ll ans;
int x, y, z, p;
int a[maxn], b[maxn], sb[maxn], sa[maxn];
int k[maxn];struct Segment_tree_min
{struct tree{int l, r;int v;int laz;}tr[maxn << 2];void pushup(int p){tr[p].v = min(tr[p << 1].v, tr[p << 1 | 1].v);}void pushdown(int p){if(tr[p].laz == 0) return ;tr[p << 1].v += tr[p].laz;tr[p << 1].laz += tr[p].laz;tr[p << 1 | 1].v += tr[p].laz;tr[p << 1 | 1].laz += tr[p].laz;tr[p].laz = 0;}void build(int p, int l, int r){tr[p].l = l, tr[p].r = r;if(l == r) {tr[p].v = sa[l];return;}int mid = l + r >> 1;build(p << 1, l, mid);build(p << 1 | 1, mid + 1, r);pushup(p);}void modify(int p, int l, int r, int v){if(tr[p].l >= l && tr[p].r <= r) {tr[p].v += v;tr[p].laz += v;return ;}int mid = tr[p].l + tr[p].r >> 1;pushdown(p);if(l <= mid) modify(p << 1, l, r, v);if(r > mid) modify(p << 1 | 1, l, r, v);pushup(p);}int query(int p, int l, int r){if(l > r) return 0;if(tr[p].l >= l && tr[p].r <= r) return tr[p].v;int mid = tr[p].l + tr[p].r >> 1;pushdown(p);int res = 1e9;if(l <= mid) res = min(res, query(p << 1, l, r));if(r > mid) res = min(res, query(p << 1 | 1, l, r));return res;}
}f;struct Segment_tree_max
{struct tree{int l, r;int v;int laz;}tr[maxn << 2];void pushup(int p){tr[p].v = max(tr[p << 1].v, tr[p << 1 | 1].v);}void pushdown(int p){tr[p << 1].v += tr[p].laz;tr[p << 1].laz += tr[p].laz;tr[p << 1 | 1].v += tr[p].laz;tr[p << 1 | 1].laz += tr[p].laz;tr[p].laz = 0;}void build(int p, int l, int r){tr[p].l = l, tr[p].r = r;if(l == r) {tr[p].v = sa[l];return;}int mid = l + r >> 1;build(p << 1, l, mid);build(p << 1 | 1, mid + 1, r);pushup(p);}void modify(int p, int l, int r, int v){if(tr[p].l >= l && tr[p].r <= r) {tr[p].v += v;tr[p].laz += v;return ;}pushdown(p);int mid = tr[p].l + tr[p].r >> 1;if(l <= mid) modify(p << 1, l, r, v);if(r > mid) modify(p << 1 | 1, l, r, v);pushup(p);}int query(int p, int l, int r){if(l > r) return 0;if(tr[p].l >= l && tr[p].r <= r) return tr[p].v;int mid = tr[p].l + tr[p].r >> 1;pushdown(p);int res = 0;if(l <= mid) res = max(res, query(p << 1, l, r));if(r > mid) res = max(res, query(p << 1 | 1, l, r));return res;}
}g;int main()
{scanf("%d%d%d%d%d", &n, &x, &y, &z, &p);for (int i = 1; i <= n; ++ i) a[i] = (1ll * (i - x) * (i - x) % p + 1ll * (i - y) * (i - y) % p + 1ll * (i - z) * (i - z) % p) % p;for (int i = 1; i <= n; ++ i)sa[i] = sa[i - 1] + a[i];scanf("%d%d%d%d%d%d%d", &m, &k[1], &k[2], &x, &y, &z, &p);for (int i = 3; i <= m; ++ i)k[i] = (1ll * k[i - 1] * x % p + 1ll * k[i - 2] * y % p + z) % p;if(m == 0) return 0;f.build(1, 1, n);g.build(1, 1, n);for (int i = 1; i <= m; ++ i){int l, r;scanf("%d%d", &l, &r);k[i] = min(k[i], f.query(1, r, n) - g.query(1, 1, l - 1));f.modify(1, r, n, -k[i]);g.modify(1, l, n, -k[i]);printf("%d\n", k[i]);}return 0;
}

BZOJ 2138 stone(霍尔定理推论,线段树)【BZOJ 修复工程】相关推荐

  1. AT2645 [ARC076D] Exhausted?(Hall定理推论/线段树+扫描线)

    AT2645 [ARC076D] Exhausted? 对于一个二分图左边点连接的是右边点的一个前缀和一个后缀,求解最大匹配. 首先不能直接求解最大匹配,但是我们可以利用Hall定理的推论求解 ∣U∣ ...

  2. BZOJ #3064. Tyvj 1518 CPU监控(线段树,历史最值)

    BZOJ #3064. Tyvj 1518 CPU监控(线段树,历史最值) Solution 我们考虑用线段树维护此题. 先不考虑历史最值. 大概需要维护一种特殊的懒标记(x,y)(x,y)(x,y) ...

  3. 子段乘积(逆元费马小定理)+线段树做法

    题解:一开始做这个题的时候想过尺取法,但是因为没有逆元的知识,不知道该如何不断删除左端元素.其实这题并不难想,设l,r为两端开始都置为1,当长度小于k的时候不断乘右端元素并取余,当长度等于k时删除左端 ...

  4. BZOJ.4695.最假女选手(线段树 Segment tree Beats!)

    题目链接 区间取\(\max,\ \min\)并维护区间和是普通线段树无法处理的. 对于操作二,维护区间最小值\(mn\).最小值个数\(t\).严格次小值\(se\). 当\(mn\geq x\)时 ...

  5. BZOJ 4719: [Noip2016]天天爱跑步 线段树合并

    title BZOJ 4719 LUOGU 1600 简化题意: 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每 ...

  6. 2019多校第九场 HDU6681 Rikka with Cake(欧拉图论定理,线段树)

    链接:HDU6681 Rikka with Cake 题意: 给出一个笛卡尔坐标系中左下角坐标为(0,0)(0,0)(0,0),右上角坐标为(n,m)(n,m)(n,m)的矩形,有K  (≤105)K ...

  7. bzoj 2653 middle (可持久化线段树)

    middle Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 1981  Solved: 1097 [Submit][Status][Discuss] ...

  8. 【BZOJ 3165】 [Heoi2013]Segment 李超线段树

    所谓李超线段树就是解决此题一类的问题(线段覆盖查询点最大(小)),把原本计算几何的题目变成了简单的线段树,巧妙地结合了线段树的标记永久化与标记下传,在不考虑精度误差的影响下,打法应该是这样的. #in ...

  9. BZOJ 5394 [Ynoi2016]炸脖龙 (线段树+拓展欧拉定理)

    题目大意:给你一个序列,需要支持区间修改,以及查询一段区间$a_{i}^{a_{i+1}^{a_{i+2}...}}mod\;p$的值,每次询问的$p$的值不同 对于区间修改,由线段树完成,没什么好说 ...

最新文章

  1. 香帅的北大金融学课笔记2 -- 银行
  2. 苹果手机的计算机删除了怎么恢复,苹果手机电话删除了怎么恢复
  3. linux shmmax单位,Linux核心参数Shmmax,shmall,shmni
  4. python 对redis key的基本操作
  5. Valid Number 1
  6. oracle10g遇到ORA-16038日志无法归档问题
  7. 梦断代码阅读笔记之二
  8. Computer Browser服务自动停止
  9. 在linux中查看服务,linux中怎么查看服务状态
  10. 倪光南:网络不安全要挨打 不用县县都建数据中心
  11. OSG 添加文字(显示中英文)
  12. python xlsx 转csv
  13. java aes ebc_Delphi XE2+标准AES加解密算法(AES/EBC,CBC/PKCS5Padding-base64)
  14. java cardlayout性能_java布局管理之CardLayout简单实例
  15. 学生环境网页设计模板下载 保护环境大学生HTML网页制作作品 简单环境网页设计成品 dreamweaver学生网站模板
  16. 数据库读写分离的理解
  17. pyqt 使用问题总结
  18. 【力扣周赛】第342场周赛
  19. VUE 爬坑之旅 -- 用 ES6 语法写一个工具类,并全局引用
  20. 实现不同海拔高度空气参数自由

热门文章

  1. ESI世界大学排名:371所内地高校上榜!
  2. 基于卷积神经网络的垃圾图像分类算法
  3. 别再折腾开发环境了,一劳永逸的搭建方法
  4. OpenCV中使用YOLO对象检测
  5. 链表问题5——反转部分单向链表
  6. 查看当前正在运行的python进程
  7. 网站优化基础教程:发布外链常见的五种方式!
  8. java.sql.SQLException: java.lang.StackOverflowError
  9. mingw32-gcc.exe: error: CreateProcess: No such file or directory
  10. 深入理解JavaScript系列(23):JavaScript与DOM(上)——也适用于新手