<h1 style="font-size:40px;">长郡第3次集训总结</h1>
写在之前
OJ地址问题
如果 oj.kuang.cf 访问不通就用 ojj.kuang.cf
关于OJ的推荐
赵康老师搭建的OJ
地址:120.79.19.114
基本系统:UOJ-System
配置:1Ghz/1G
优点:随时都在线,不会出现无法访问情况
我搭建的OJ
地址:oj.kuang.cf
地址:oj.zendee.cn
地址:ojj.kuang.cf
两个地址哪个访问的通就访问哪个吧
基本系统:QingDaoOJ
配置:2.6Ghz/4G
缺点:目前暂时搭建在本机虚拟机中,还没有转移到服务器上,所以我没开机的时候无法访问
阅读本总结的须知
关于题目编号
以CJ开头的题目编号是长郡集训考试的题目
以P或者CF或者UVA之类的题目开头的题目是自己在洛谷上刷题的题目编号
本题解提供我自己OJ和洛谷上题目的链接,可以随意在哪个上面测评
关于标程代码
会在题解最后附上,建议看了思路过后自己写,写不出来再看标程
长郡集训总结
CJ190725A
题目链接
http://oj.kuang.cf/problem/190725A
https://www.luogu.org/problemnew/show/U79299
基本思路
转换思路,把一个位置能管理到某些人转换为一个位置能被某些人管理到
至于怎么查询一个人能被哪些人管理到,直接树上倍增就行了
标记一个人能被哪些人管理到,树链剖分或者树上差分都可以
涉及知识点
树上倍增,树上差分,深度优先搜索
CJ190725B
题目链接
http://oj.kuang.cf/problem/190725B
https://www.luogu.org/problemnew/show/U79300
基本思路
分析题目,你会发现这似乎是一道三维偏序的题目,然后想用CDQ分治去完成
但是CDQ分治求解三维偏序问题的复杂度是$nlog^2n$ 在这题中只能过掉 $70\%$ 数据
冷静分析题目可以发现题目有一个特殊性质,那就是每一种能力值都不会有重复,也就是对于每一种能力值,都是一个排列。
所以考虑容斥原理,因为现在是要求同时满足三个条件的点对的个数,我们不妨先求出所有两两满足条件的点对个数。大体思路是这样,具体过程详见标程代码。
标程代码我每个部分分都写出来了,可以参考一下
//正解 容斥
//S(i,j) = (pp[i].a > pp[j].a) + (pp[i].b > pp[j].b) + (pp[i].c > pp[j].c)
//M(i,j) = max{S(i,j) , S(j,i)}
//a: cnt whose [M(i,j) = 2]
//b: cnt whose [M(i,j) = 3]
//a + b = n * (n - 1) / 2;
//令 TT 为两两逆序对组数,即 (pp[i].a > pp[j].a && pp[i].b > pp[j].b) + (pp[i].a > pp[j].a && pp[i].c > pp[j].c) + (pp[i].c > pp[j].c && pp[i].b > pp[j].b)
//TT = 3*b + a
//TT - n * (n-1) / 2 = 2*b --> b = TT / 2 - n * (n-1) / 4
涉及知识点
CDQ分治求三维偏序,容斥,树状数组,排序
CJ190725C
题目链接
http://oj.kuang.cf/problem/190725C
https://www.luogu.org/problemnew/show/U79301
基本思路
贪心 + DP
涉及知识点
思维,动态规划
CJ190725D
题目链接
http://oj.kuang.cf/problem/190725D
https://www.luogu.org/problemnew/show/U79302
基本思路
这题我没做出来
但是题解说是:
期望DP+贪心。设F[i]表示在i的子树中找到壳要走的期望步数,G[i]表示在i的子树中没找到壳回来要走的期望步数
儿子向父亲转移时,用贪心考虑先走哪个儿子即可
CJ190723A
题目链接
http://oj.kuang.cf/problem/190723A
https://www.luogu.org/problemnew/show/U78916
基本思路
其实这道题部分分暗示你做法很明显
我都是看了部分分,想部分分解法然后想出了正解
观察subtask1的任务特征,w 和 X 只有两种取值,1 和 2
首先一个很容易想到的是,读入的所有大于X的边权插入图中是没有意义的,你把图中所有大于X的边删除也不会对答案有任何影响
所以我们读入的时候索性不插入大于X的边
接下来分类讨论,$X=1$的时候,把所有$w>1$的边删掉,很明显就是问你图中每一个连通块中点对的个数,你可能会想计算答案用 $cnt * (cnt-1) / 2$ 计算,其中cnt为每个连通块的点个数
那么现在$X=2$呢,由于$w≤2$,所以肯定删不掉边了,那么我们自然想到把$w=1$的边先加进去,形成若干连通块,这个时候每个连通块内没有任何点对满足限制,答案暂时为0。然后你再尝试将$w=2$ 的边加进去,这个时候你就会惊奇的发现,你联通任意两个连通块,对答案的贡献实际上就是两个连通块点个数相乘
所以你想出了正解,正解就是先加入所有$w<X$的边,形成若干连通块。然后加入所有$w=X$的边,计算每一条边加进去的贡献。
那么维护连通块点的个数,你也许可以用dfs或者set。但是这里提供一个更简单的方法那就是并查集+维护size,具体可网上搜索介绍
涉及知识点
观察数据范围想正解,并查集维护大小
CJ190723B
http://oj.kuang.cf/problem/190723B
https://www.luogu.org/problemnew/show/U78918
基本思路
这道题很简单,大脑里面稍微想象一下情景或者手造几颗树就可以得到答案了。
重点是如何找点对$(i,j)$形成的路径中(这里我规定i,j在同一条链上即他们的lca=i或者j),较浅的一个节点的儿子,这个其实倍增一下就好了
涉及知识点
倍增求LCA,倍增思想,树链剖分
CJ190723C
http://oj.kuang.cf/problem/190723C
https://www.luogu.org/problemnew/show/U78919
基本思路
这题主要是思维,对于是$s_1->t_1,s_2->t_2$的最短路径,只可能有两种情况,一种是他们中间有一部分公共的路径,一种是他们中间没有任何一部分公共路径
对于有公共路径的情况,你只需要枚举公共路径的起始节点,这样计算一次必须要的线缆是多少。实际上题目求切断的线缆尽量多就是要让你保留线缆尽量少。
所以所有你算出的结果取min就好了
至于求最短路,你大可使用n次$dijkstra$,但是介于超时可能性极大,而且图中每个边边权都是1,你为啥不用n次BFS求最短路呢?
涉及知识点
广度优先搜索,类似DP?,思维
CJ190723D
http://oj.kuang.cf/problem/190723D
https://www.luogu.org/problemnew/show/U78920
基本思路
这题主要是生成树加边,但是有撤销操作,而路径压缩版本的并查集是不支持撤销操作的
这里介绍并查集的另一种优化方式,那就是按秩合并并查集,也叫启发式合并并查集,它可以支持你回退最后k次合并操作,很符合这题的要求
知道这个知识点后,写这道题也就很简单了,其余细节参考官方题解
http://vfleaking.blog.uoj.ac/blog/15
涉及知识点
按秩合并并查集,离线处理操作
CJ190726A
http://oj.kuang.cf/problem/190726A
https://www.luogu.org/problemnew/show/U79486
基本思路
这道题有很暴力的BFS搜索方法,官方题解也是给的暴力的BFS搜索方法
当然我也有很暴力的DP方法,设$DP[sta]$ 表示用给定的n个树,乱七八糟异或一通,乱七八糟把某一位改一通,能够达到sta状态的最小步数,那么很明显答案就是$min\{ cnt(s ⊕ t),dp[s ⊕ t]\}$ 其中⊕表示异或,然后cnt表示求这个数的二进制下有多少1
至于如何DP嘛,类似于背包问题?你可以看看标程,用队列实现的DP
涉及知识点
动态规划,广度优先搜索,思维
CJ190726B
http://oj.kuang.cf/problem/190726B
https://www.luogu.org/problemnew/show/U79488
基本思路
一道字符串DP题,思路转换为找一个原来串里面出现过的最短的串,满足可以在后面接上一个在原串中后面没有出现过的字符
DP+贪心
最短不出现序列必然是一个出现过的序列(可以为空)+一个字母
设fi表示在i位置,往后第一个字母ch在哪里。
设dp[i]表示在i位置,造出最短不出现序列所需长度
然后倒着DP得出答案,正着贪心输出解即可。
涉及知识点
动态规划,贪心
CJ190726C
http://oj.kuang.cf/problem/190726C
https://www.luogu.org/problemnew/show/U79490
基本思路
一道很好的欧拉反演欧拉函数的题目,重点在于你要推出之前的式子
看到要用欧拉函数先别慌着跳下一道题,这题用到的化简知识点非常套路,记住了就可以,思维难度比较低
但是真正难的是原始式子的推导,对于在同一行或者同一列的三个点的答案贡献,实际上就是$ans1=m*C_m^3 + n * C_m^3$
对于斜着的三个点,容易发现,实际上你就是要在原来的点阵图中框出若干个直角三角形,在这个三角形的斜边上碰到多少个点,答案就加上这么多点×2,为什么×2?因为这里规定三角形直角一定在图左下角,×2相当于把三角形对称一下,计算答案
枚举三角形可以考虑只枚举两条直角边的长度,然后平移,具体推导过程可以自己推一下,最终的结果就是:
$ans2 = 2\sum\limits_{i=1}^{n-1}\sum\limits_{j=1}^{m-1}(n-i)(m-j)(gcd(i,j)-1)$
看到式子里面有gcd,然后又是$O(n,m)$的复杂度,你是不是慌了?实际上,对于处理这里类问题有一个通用的碰运气化简方法,其核心是下面这个式子
对于任意一个正整数x,$\sum\limits_{d|x}φ(d) = x$
证明?自己百度吧,这是欧拉函数的最基本性质之一
所以我们把$gcd(i,j)$ 当成 上面式子中的 x,就可以化简 ans2 为
$ans2 = 2\sum\limits_{i=1}^{n-1}\sum\limits_{j=1}^{m-1}(n-i)(m-j)(\sum\limits_{d|gcd(i,j)}φ(d)-1)$
把后面的那个减1拿出来,就比较爽
$ans2 = 2(\sum\limits_{i=1}^{n-1}\sum\limits_{j=1}^{m-1}(n-i)(m-j)\sum\limits_{d|gcd(i,j)}φ(d) - \sum\limits_{i=1}^{n-1}\sum\limits_{j=1}^{m-1}(n-i)(m-j))$
然后是一个技巧,更换枚举顺序,把通过枚举i,j确定d改为通过枚举d来确定i,j来进一步化简时间复杂度
式子就可以写成这样子,搞不懂的同学多想想怎么由i,j算d的,返回去怎么通过d推出i,j
$ans2=2(\sum\limits_{d=1}^{min(n-1,m-1)}φ(d)\sum\limits_{i=1}^{\lfloor\frac{n-1}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m-1}{d}\rfloor}(n-di)(m-dj) - \sum\limits_{i=1}^{n-1}\sum\limits_{j=1}^{m-1}(n-i)(m-j))$
然后又一个重要的技巧,那就是形如这样的式子
$\sum\limits_{i=x1}^{y1}\sum\limits_{j=x2}^{y2}i*j$
注意,这里后面不一定是i*j,只要是后面是两个等差数列相乘的,都可以O1算,因为这个式子可以调换顺序,变成这个样子
$\sum\limits_{i=x1}^{y1}i * \sum\limits_{j=x2}^{y2}j$
两个求和都是等差数列求和,高斯求和不用多说了吧,然后相乘
所以上面的ans2的后面的两坨,都可以O(1)求得,整体复杂度O(min(n-1,m-1))
涉及知识点
欧拉函数化简,套路性化简,等差数列求和,数论
CJ190726D
http://oj.kuang.cf/problem/190726D
https://www.luogu.org/problemnew/show/U79491
基本思路
我做不来,看官方题解吧。
第二类斯特林数+prufer序列+推式子
涉及知识点
第二类斯特林数+prufer序列+推式子
CJ190729A
http://oj.kuang.cf/problem/190729A
https://www.luogu.org/problem/U80110
基本思路
简单送分题,将整数q分解质因数过后,所有因数个数只有为2的时候Bob胜利,否则Alice胜利
因为无论如何,只要对方取数过后留给你的因数个数还剩2个,你就输了,你只能取一个,然后对方就取不到了
所以除非先手Alice一来就被拥有两个因数,否则她都可以只给Bob剩下2个因数,让自己赢
涉及知识点
质数筛法,分解质因数,思维,简单博弈
CJ190729B
http://oj.kuang.cf/problem/190729B
https://www.luogu.org/problemnew/show/U80111
基本思路
考虑贪心的思维,如果一个视频的流量可以同时杀死多个蔡徐坤,那么肯定必须物尽其用,杀死流量需求最大的蔡徐坤。所以暴力想法就是把蔡徐坤们按照流量排序,然后把鬼畜视频按照花费排序,从小到大的枚举每个视频,对于每个视频能杀死一堆蔡徐坤,就杀死流量最大的蔡徐坤
但是时间复杂度明显不对,考虑如何优化。看题目数据范围,蔡徐坤需要的流量和视频流量都在$10^6$内,那么完全可以用桶来存,不知道桶是什么的,可以把桶看成一个数组,数组每个位置存流量为其下标的物品。如果是蔡徐坤桶,那么每个位置存当前下标流量下有多少个蔡徐坤;如果是鬼畜视频桶,实际上是一个$10^6$大小的vector,因为同一个花费的视频可能有多个。桶的优化在于,你可以在很快的时间$(O(n))$内从小到大遍历,而不必花费$O(nlogn)$去排序,这也算是用空间换时间的一种方式吧。
第二个优化是,如何找到所有能被该视频杀死的蔡徐坤里面,流量最大的蔡徐坤,然后把他杀死?其实你可以用链表跳,但是更快也更直观的方式是用路径压缩版本的并查集。试想蔡徐坤桶里面每个下标对应并查集中的一个元素:如果一个桶里面有值(即所有存活的蔡徐坤里面有这个流量的蔡徐坤),那么这个元素$i$的$fa[i]=i$,表示这个地方有值;如果一个桶里面没有值(即所有存活的蔡徐坤里面没有这个流量的蔡徐坤),那么这个元素$i$的$fa[i]=get(i-1)$表示这个地方没有值,要找流量更小的蔡徐坤就去前面找。
然后对于每个视频,只需要在蔡徐坤桶里面去用$fa[]$跳就行了
具体参考标程
涉及知识点
路径压缩并查集,桶,贪心思想,优化思想
鸡你太美!
Codechef REIGN
https://cn.vjudge.net/problem/CodeChef-REIGN
基本思路
先$O(n)$的扫描两遍数组,第一遍从头到尾预处理出对于$i$结尾的前缀,最大的连续和$pre[i]$,第二遍从尾到头预处理出对于$i$开头的后缀,最大的连续和$suf[i]$。处理可用以前的贪心思想处理
然后$O(n)$的枚举一个点$p$,在这个点前的一段区间里面找一段最大连续和,即$pre[p]$,再在这个点后$p+k+1$的位置找到一段最大连续和(因为题目规定两段必须相差大于$k$),即$suf[p+k+1]$,对于所有枚举的点$p$取最大值即可
涉及知识点
预处理,贪心
CodeForces 1037E
https://cn.vjudge.net/problem/CodeForces-1037E
基本思路
考虑转换思想,将每条边加进来就得一次答案转换为先把所有边加进来再删边。于是我们可以先离线所有操作,然后算出最后的答案,注意中途维护每个点的度数$degree[i]$。
然后倒着删除每条边,对于删除的每条边,有两个点会度数减少,同时造成很多人无法去旅行,造成的影响可以用BFS搜索得到。注意一点,对于一个已经无法去旅游的人,直接标记,下次搜索就不用管他了,因为管他或者不管他都不会影响答案。
涉及知识点
广度优先搜索,思维转换,离线询问
Codechef DIGJUMP
https://cn.vjudge.net/problem/CodeChef-DIGJUMP
基本思路
这题其实本来就是暴搜,不过纯暴搜是过不去的,需要一点点优化。
稍微读题就知道,其实这个题可以把跳的过程看成是图上行走的过程,你会发现暴力跑图复杂度不对,所以如何优化?考虑到对于权值相同的节点,如果在图上其实是两两相连的,也就是一大坨块,我们可以在心中把他们缩成一个点,然后再在图上跑。当然实际搜索的时候也不是真正的建图出来跑,只是模拟那个过程,同一个权值的一堆点只会入队一次,出队后就再也不会push进来了,所以复杂度有了保证
涉及知识点
广度优先搜索,图形转化思想,优化思想
Codechef PERMPART
https://cn.vjudge.net/problem/CodeChef-PERMPART
基本思路
这题的解法非常神奇,首先我先不说这道题,单纯讲一个更普通的问题,问题如下
给你一个长n的数组a[i],你可以花费1块钱把一个位置+1,或者-1,问将原数组修改成单调不升序列的最小花费(n<=100000)
这个问题的题解在这里:https://blog.csdn.net/can919/article/details/79068186
你可能会想到设$dp[i][j]$ 表示做到第$i$个数,最后一个位置为$j$的时候的最小花费,然后转移为$dp[i+1][j]=min(dp[i][k])+|j-a[i]|;$
但是分析一下可以发现,这个dp的转移复杂度是不对的,即使是将后面转移用神奇方法优化到$O(1)$复杂度,你前面的枚举也是$O(n^2)$,所以这种方法是不太科学的
然后题解里面用了很大的篇幅来讲证明以及一些内部原理,其实我也不是太懂,但是我想说的就是,这个问题的最终结论,也就是固定解法:优先队列,就当作模板去记一记也未尝不可
priority_queue<int,vector<int>,greater<int> > pq;
int ans;
for(int i=1;i<=n;i++){
pq.push(a[i]);
if(!pq.empty() && pq.top() < a[i]){
ans += a[i] - pq.top();
pq.pop();
pq.push(a[i]);
}
}
然后回到这道题,可以发现,数字的位置与你最后求得的答案实际上是无关的,因为题目只要求你拆分出一个个的排列,不要求这些排列要连续,所以位置是可调的。有一个东西很容易想到,那就是如果题目中给出的数据中有$a[i]>2*n$了,如果是要通过添加数字的方法去构成排列,显然需要大于$n$的代价,显然将它删除更划算。
所以对于所有$a[i]>2*n$的数,我们的决策是直接删除
然后是剩下的数,我们可以将它们放在一个桶里面,记录每个数出现的次数。现在看出这道题和我之前举例的那个题的关系了吗?是的,这道题现在问题转变为让你求最小代价,使得桶中的元素单调不降,这样才满足可以从中分出若干个排列,很简单的想想就可以明白。
涉及知识点
最小代价求单调不降(升)子序列,桶计数,思维转换
ZJOI2007 仓库建设
http://ojj.kuang.cf/problem/12
https://www.luogu.org/problem/P2120
基本思路
一道斜率优化DP题,只需要大力推式子就好了,如果不会推斜率优化式子的,请看我的《斜率优化DP学习笔记》
首先我们可以设$dp[i]$表示在第$i$个工厂设立仓库,前$i$个工厂最小的花费。那么容易推出转移方程
$dp[i]=C_i + min\{dp[j]+\sum\limits_{k=j+1}^{i-1}P_k(X_i-X_k)\}(j<i)$
把括号中打开可以得
$dp[i]=C_i+min\{dp[j]+X_i\sum\limits_{k=j+1}^{i-1}P_k-\sum\limits_{k=j+1}^{i-1}P_kX_k\}$
我们发现式子中有两个求和符号,都可以用前缀和优化到$O(1)$得求和符号值
所以不妨设$F(x)=\sum\limits_{i=1}^xP_i$,$T(x)=\sum\limits_{i=1}^xP_iX_i$
原始可以化简为
$dp[i]=C_i+min\{dp[j]+X_i*(F(i-1)-F(j))-(T(i-1)-T(j))\}$
现在问题转换为要求后面一坨表达式对于所有$j<i$的最小值
暴力求解的话,对于每一个$i$计算最小值为$O(n)$,整题复杂度为$O(n^2)$,明显过不去,考虑斜率优化转移,开始推式子
设决策$k_1<k_2$,且$k_2$优于$k_1$
用式子写出来即$dp[k_1]+X[i]F[i-1]-X[i]F[k_1]-T[i-1]+T[k_1]>dp[k_2]+X[i]F[i-1]-X[i]F[k_2]-T[i-1]+T[k_2]$
左右两边约去相同项,将与 $i$ 无关项移到不等式一侧,可以得到:
$dp[k_1]-dp[k_2]+T[k_1]-T[k_2]>X[i](F[k_1]-F[k_2])$
将所有与 $i$ 无关的项放在不等式一边,注意这里我们之前假设的是$k_2>k_1$,所以这里$F[k_1]-F[k_2]<0$,除过去要变号
$\frac{dp[k_1]-dp[k_2]+T[k_1]-T[k_2]}{F[k_1]-F[k_2]}<X[i]$
注意到左侧分子部分可以抽象为一个函数
不妨令$M(x)=dp[x]+T[x]$
于是式子可以化成标准斜率式:$\frac{M(k_1)-M(k_2)}{F[k_1]-F[k_2]}<X[i]$
然后就可以斜率优化转移dp了
涉及知识点
斜率优化,前缀和
CJ190805A
http://ojj.kuang.cf/problem/16
基本思路
考虑将输入的所有牌按照花色分组,对于每一种花色,按照牌的数字大小排序并去重。原题就相当于在每种花色中找出连续一段,使得这一段(最大值-最小值+1) <= n,并且这一段长度尽量长,那么答案就是n-每一个段长度的最大值
具体实现分组可离散化,查一段可以二分或倍增
涉及知识点
二分,倍增,离散化
CJ190805B
http://ojj.kuang.cf/problem/17
基本思路
顺次枚举每一个$a[i]$,顺便把$i$之前的数维护一个$last[j]$表示在$i$之前,最后一次拥有$j$这个子集的数出现的位置
然后查询的时候就可以枚举$a[i]$的每个子集$sub$,判断$last[sub]$是否在$[i-b[i],i]$范围内,如果不在这个范围内,则这个子集贡献+1
枚举一个数的所有子集,可以用以下方法(枚举$x$的子集)
for(int sub=x;sub;sub=(sub-1)&x){
//sub 就是枚举的子集
}
涉及知识点
枚举子集,思维
CJ190805C
http://ojj.kuang.cf/problem/18
基本思路
分析题目可以知道,如果几个大楼能够两两相互到达,即几个大楼在同一个强连通块内,那么超人如果可以救一个楼的火,就可以救这个块内其他楼的火,所以考虑$tarjan$缩点,将原图转换成$DAG$
缩点完后的新图中,每个点的点权就对应原图中联通块所有点的权值和
然后问题转换为从起点$S$开始,经过某些点过后到指定点集$P$,使得经过的点点权尽量大。
由于是$DAG$,所以有很多特殊性质可以用到,比如我们就可以把点权转换到连向他的边上,使得求点权最大变为$DAG$上求最长路问题
对于$DAG$最长路问题,有几个固定解法,各有优劣,下面列举最常用两种
SPFA
将边权设置为原边权的相反数,然后跑最短路,由于是$DAG$不存在环,所以可以放心SPFA,然后结果再取相反数就是最长路
注意的是,这个方法可能会被卡时间
拓扑排序
$DAG$上求最长路的标准做法就是拓扑排序,类似于$BFS$搜索,首先把入度为0的所有点push进队列,然后依次对队列中每个点向周围搜索,然后周围点度数-1,如果减了后度数为0了,就再push进队列里面继续搜索。
最后可以求出$DAG$上的最长路
涉及知识点
$tarjan$缩点,$DAG上最长路$
BZOJ1303
https://cn.vjudge.net/problem/HYSBZ-1303
基本思路
分三种情况,一种是以b所在位置为开头,往后拓展,长度为奇数且以b为中位数的;一种是以b所在位置为结尾,往前拓展,长度为奇数且以b为中位数的;一种是将b包含在中间,以b为中位数的
我们把整个序列重新赋值一下,大于b的数赋值为1,小于b的数赋值为-1,b本身赋值为0。前两种情况就很好处理了,直接从b开始向前向后扫一遍就行了,加到0就说明当前序列合法。现在考虑将b包含在中间的情况,也就是b前选一段,b后选一段,使得两段和加起来为0,这个只需要在之前扫描的时候用桶记录一边出现的可能的和的个数,由于可能出现负数,可以用map存或者全部加上一个数使得其为正数,然后再扫另外一边的时候检查有没有在桶里有出现当前和的相反数,如果出现了,那么必然相加为0,是合法情况,所以答案加上桶中的个数
涉及知识点
思维,map
BZOJ2783
https://cn.vjudge.net/problem/HYSBZ-2783
基本思路
这道题问题是:给定一棵树,和一个值$S$,每个节点有一个权值,问你整棵树有多少条路径覆盖的节点权值总和刚好为$S$
实际上换个角度考虑,这个问题变为:对于每个节点,是否能够在该节点祖先地方找到一个节点,使得他们之间(包括自身)的权值加起来为$S$。凡是与祖先有关的问题,首先就可以思考一下倍增,这道题倍增的确是可以做的。首先找到树的根,读入时维护每个节点入度,入度为0即为根,然后以这个根开始dfs,维护每个节点(包括自身)到根节点路径上点权值和,然后维护倍增数组,对于每个点,就往上跳,找到路径点权和小于等于S的最浅节点,如果能找到这个节点,答案+1就可以了
涉及知识点
倍增,思维转换
BZOJ3631
https://cn.vjudge.net/problem/HYSBZ-3631
基本思路
这个题的题意就是维护一棵树的点权,最初都是0,然后有一种操作,那就是$modify(x,y)$,表示将$x,y$简单路径上每个点权都+1,多次操作,问你最终每个点的点权
像这种树上区间修改的问题,我们有两种做法,一种是树链剖分后建线段树维护,这种方法优势在于支持操作比较多,线段树支持的操作它都支持,缺点是很难写,而且常数大。另一种方法是树上差分,差分需要求LCA,所以需要用倍增或者树剖求LCA。
这里因为只涉及区间加操作,所以果断树上差分,差分过程是这样的。
假设要修改$x,y$路径上的点权,全部加上$w$,设$c = lca(x,y),fc=fa[c]$
只需要$dt[x]+=w,dt[y]+=w,dt[c]-=w,dt[fc]-=w$
然后求最终的数组的时候,这样dfs一遍就求出来了
void dfs3(int u){
for(int i=p[u];~i;i=E[i].nxt){
int v = E[i].v;
if(v == fa[u]) continue;
dfs3(v);
dt[u] += dt[v];
}
}
涉及知识点
倍增,树链剖分,树上差分
P1083 借教室
https://www.luogu.org/problem/P1083
基本思路
转换一下问题,这道题实际上就是给定一个数组,你需要实现一下操作:区间查询最小值,区间加上一个数(这道题实际上是区间加上负数),所以直接一个带lazy标记的线段树就可以实现了,具体看标程
涉及知识点
线段树+lazy标记
P2161 [SHOI2009]会场预约
https://www.luogu.org/problem/P2161
基本思路
题目大意:不断加线段,每次将线段覆盖的其他线段删除(没覆盖完也要删除),并问你每次删除线段条数或者剩余线段总数
要实现这道题需要的操作,实际上就是在加入一条线段的时候,计算有多少线段被删除了。线段被删除的条件是要么完全被新加入的线段覆盖,要么是新加入的线段的左右端点在这个欲删除线段内。于是我们可以开一个数组,对于每一条线段,我们给予他一个ID,然后在数组中它对应区间的值全部设置为这个ID
然后问题转换成,区间更新,区间查询不同ID的个数是多少,这个可以用线段树来做,只不过合并过程要判断一下,线段树每一个节点需要维护:lv该区间最左边的值是多少
,rv该区间最右边的值是多少
,cnt这个区间包含多少ID不同的区间
。合并区间$a,b$的时候,结果的$res.cnt=a.cnt+b.cnt$,但是如果$a.rv==b.lv$,那么实际上合并过后中间是一段,所以还要$res.cnt--$
这题很多细节问题,需要自己手写线段树感受,debug我de了很久...
涉及知识点
线段树+lazy标记,自定义线段树区间合并,题意转换
P1880 [NOI1995]石子合并
https://www.luogu.org/problem/P1880
基本思路
基本题意:一个环,相邻两堆石子可以合并到一起,每次花费石子个数和这么多体力,求最小体力和最大体力将所有石子合成一堆
以前是用小根堆贪心做的,后来发现好像有问题。所以这次试试区间dp的做法,毕竟区间dp也是dp中很重要的知识点
对于区间dp的问题,通常我们的状态都很单一,就是$dp[i][j]$表示区间i~j的xxx答案,最多也就在后面添个几维附加状态,也很好想。如果你看到$n$范围特别小,而且是关于区间的问题的时候,你就要往区间dp方向去想了。
这道题,典型的区间dp问题,设$mindp[i][j]$表示合并区间i~j的范围内的石子,最小花费,$maxdp[i][j]$同理。由于这是环,所以我们化环为链,把数组复制一遍接在原数组后面,这样就可以在链上dp啦。
转移方程就是$mindp[i][j]=mindp[i][k] + mindp[k+1][j] + prea[j] - prea[i-1]$,其中k为我们枚举的i~j区间内的一个中间点,$prea[]$表示复制完的数组的前缀和。由此可以看出套路,区间dp的进行实际上就是小区间不断合并或者拓展成大区间的过程,至于区间dp代码的实现,可以参考标程。
涉及知识点
区间dp
计蒜客-银河战舰
https://www.jisuanke.com/course/940/253772(访问不通就看题目描述)
题目背景
瑞奥和玛德利德是非常好的朋友。瑞奥平时的爱好是吹牛,玛德利德的爱好是戳穿瑞奥吹的牛。
这天瑞奥和玛德利德来到了宇宙空间站,瑞奥向玛德利德炫耀这个空间站里所有的银河战舰都是自
己的。整个空间站可以看成一个无限大的二维平面,每个战舰都可以看做一个点,在空间站中一共
分布着 艘银河战舰。
玛德利德:“你说这些都是你的,那你让他们动一动啊”。
瑞奥:“诶你看,那艘动了!”。
玛德利德:“操作指令由我来发,一共有 5 种动的方法......”。
瑞奥:“我觉得这样有失公正......”。
题目描述
第一行一个正整数$N$,表示战舰的数量
接下来$N$行,每行两个实数,代表第$i$个战舰的$x,y$坐标
然后一个正整数$M$,代表调度的次数
接下来$M$行操作,每个操作都是如下类型的一种:
$M\ l\ r\ p\ q$:把编号在$[l,r]$区间内的战舰$x$坐标加上$p$,$y$坐标加上
$q$
$X\ l\ r$:把编号在$[l,r]$区间内的战舰沿$x$轴翻转
$Y\ l\ r$:把编号在$[l,r]$区间内的战舰沿$y$轴翻转
$O\ l\ r$:把编号在$[l,r]$区间内的战舰沿直线$y=x$翻转
$R\ l\ r\ a$:把编号在$[l,r]$区间内的战舰绕原点逆时针旋转$a°$
注意,$p,q,a$均可能为小数
输出格式
输出包括$N$行,代表着$N$艘战舰经过$M$次调度之后的坐标(保留两位小数)
样例输入
3
1 2
-2 2.5
0 -3
3
X 1 3
M 1 3 3 6
R 1 3 90
样例输出
-4.00 4.00
-3.50 1.00
-9.00 3.00
基本思路
这里涉及到维护区间内战舰的二维坐标信息,但是可以发现,一般的线段树是不能实现这样复杂的区间内让某一个坐标取相反数,或者是交换两个坐标,或者是旋转坐标,如果可以,纯线段树的方法未免也太复杂了一点。
对于这样的空间几何问题,可以考虑考虑矩阵乘法。因为矩阵乘法的本质就是空间内图形变换的过程,所以这道题可以转换为线段树区间乘法,然后最后查询每一位置的结果。只不过这里线段树的区间乘法是乘上的一个矩阵。
由于矩阵拥有结合律,即 $A * B * C*...*Z=A*(B*C*...*Z)$,所以我们只要用线段树维护好所有请求后,最终每个位置的矩阵结果(即这里的$B*C*...Z$),再用输入坐标(即这里的$A$)乘上结果矩阵就是最终答案了(即$A*B*C*...*Z$)。
下面考虑如何构造变换矩阵,首先是状态矩阵,表示某个编号战舰的状态的矩阵,即包含该战舰的坐标,具体分析操作可以知道,$M$操作是将坐标分别加上$p,q$,由于涉及到常数操作,所以我们需要在矩阵中放入一个常数$1$来实现常数的增加操作,于是构造出的状态矩阵是这样子:$\begin{bmatrix} x & y & 1\end{bmatrix}$
表示当前它的坐标是$(x,y)$,后面那个$1$正如上文所说,是用来常数操作使用的
下面来看各种操作的转移矩阵,具体推导过程不作详细叙述,只有最后一个旋转需要详细说一下。
1.操作$M$,参数$p,q$,转移矩阵为:
$\begin{bmatrix}x&y&1\end{bmatrix}*\begin{bmatrix}1&0&0\\0&1&0\\p&q&1\end{bmatrix}=\begin{bmatrix}x+p&y+q&1\end{bmatrix}$
2.操作$X$,参数无,转移矩阵为:
$\begin{bmatrix}x&y&1\end{bmatrix}*\begin{bmatrix}1&0&0\\0&-1&0\\0&0&1\end{bmatrix}=\begin{bmatrix}x&-y&1\end{bmatrix}$
3.操作$Y$,参数无,转移矩阵为:
$\begin{bmatrix}x&y&1\end{bmatrix}*\begin{bmatrix}-1&0&0\\0&1&0\\0&0&1\end{bmatrix}=\begin{bmatrix}-x&y&1\end{bmatrix}$
4.操作$O$,参数无,转移矩阵为:
$\begin{bmatrix}x&y&1\end{bmatrix}*\begin{bmatrix}0&1&0\\1&0&0\\0&0&1\end{bmatrix}=\begin{bmatrix}y&x&1\end{bmatrix}$
5.操作$R$,参数$a$,转移矩阵为:
需要特别注意这里题目中$a$给的是角度,单位为度,而c++的math函数库中的一系列三角函数比如$cos,sin,acos$等传入的参数都是弧度,单位是RAD,所以需要将输入的$a$进行转换,具体公式是:$π\ rad = 180°$,所以读入的$a=a/180*π$
还有需要补充一个旋转公式,那就是对于一个点$(x,y)$,绕原点逆时针旋转$a$的角度后过后,新的点坐标为$(x\ cosa-y\ sina,x\ sina+y\ cosa)$
所以转移矩阵就很明了了:
$\begin{bmatrix}x&y&1\end{bmatrix}*\begin{bmatrix}cosa&sina&0\\-sina&cosa&0\\0&0&1\end{bmatrix}=\begin{bmatrix}xcosa-ysina&xsina+ycosa&1\end{bmatrix}$
然后得到这些矩阵的推导,我们就可以用线段树区间乘单点查放心做了,由于矩阵乘法的结合律,所以可以证明做法没有问题。注意一下最后需要把所有的$lazy$标记全部$pushdown$下去
具体可看标程做法
涉及知识点
简单平面几何,矩阵乘法,线段树区间乘单点查
P3294 [SCOI2016]背单词
https://www.luogu.org/problem/P3294
基本思路
贪心+trie树
考虑情况1,消耗过于巨大,所以肯定不能让情况1出现。
所以考虑每个单词倒着建立trie树,维护每个单词的终端节点位置。这样一个当一个单词为另一个单词的后缀时,这个后缀在trie树上的终端结点一定是长串在trie树上终端节点的祖先。
建立完trie树后,问题转换为在trie树上对于每个终端节点,分别编号,使得每个结点的编号减去其父亲结点编号的和最小,这里的父亲节点需要特别说明,并不是trie树节点上的父亲,而是指在这个节点的所有祖先中,距离它最近的一个终端节点。
于是对于所有终端节点,可以重新建立一棵只含终端节点的新树,而维持原trie树上的父子关系。贪心的想,为了达到最小和,肯定优先走size较小的子树更优。所以我们可以建完新树过后,维护每个节点儿子子树的size,然后优先走size较小的儿子,并编号计算答案。
实现方面,建立新树如何寻找一个节点的父亲(含义同上),可以dfs原trie树时用一个栈记录上一个访问的终端节点。如何存储新树中每个节点的儿子还要从小到大访问,这个可以每次手动sort或者全局使用priority_queue,具体实现参考标程,使用的是priority_queue.
涉及知识点
贪心,字典树(trie tree),深度优先搜索,栈
题目标程
CJ190725A
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 2*100010;
const int maxm = maxn;
struct edge{
int v,nxt,w;
}E[maxm*2];
int p[maxn],eid;
void init(){
memset(p,-1,sizeof p);
eid = 0;
}
void insert(int u,int v,int w){
E[eid].v = v;
E[eid].w = w;
E[eid].nxt = p[u];
p[u] = eid++;
}
void insert2(int u,int v,int w){
insert(u,v,w);
insert(v,u,w);
}
int w[maxn];
int depw[maxn],anc[maxn][25],dep[maxn];
int dt[maxn];
int n;
void dfs(int u){
//cout << "dep:" << dep[u] << endl;
for(int i=p[u];~i;i=E[i].nxt){
int v = E[i].v;
int w = E[i].w;
if(v == anc[u][0]) continue;
anc[v][0] = u;
depw[v] = depw[u] + w;
dep[v] = dep[u] + 1;
dfs(v);
}
}
void getanc(){
for(int i=1;(1 << i) <= n;i++){
for(int j=1;j<=n;j++){
anc[j][i] = anc[anc[j][i-1]][i-1];
}
}
}
void modify(int x,int y,int w){
//dt
if(dep[x] > dep[y]) swap(x,y);
dt[anc[x][0]] -= w;
dt[y] += w;
}
void solve(int u){
int now = u;
for(int i=21;i>=0;i--){
int aanc = anc[now][i];
if(aanc == 0) continue;
if(depw[u] - depw[aanc] <= w[u]) now = aanc;
}
modify(now,u,1);
}
void calctree(int u){
for(int i=p[u];~i;i=E[i].nxt){
int v = E[i].v;
if(v == anc[u][0]) continue;
calctree(v);
dt[u] += dt[v];
}
}
signed main(){
//freopen("A3.in","r",stdin);
init();
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",w+i);
for(int i=2;i<=n;i++){
int v,w;
scanf("%lld%lld",&v,&w);
insert2(i,v,w);
}
dep[1] = 1;
dfs(1);
getanc();
for(int i=1;i<=n;i++) solve(i);
calctree(1);
for(int i=1;i<=n;i++){
if(i == 1) printf("%lld",dt[i]-1);
else printf(" %lld",dt[i]-1);
}
return 0;
}
CJ190725B
// luogu-judger-enable-o2
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2*1000010;
int n;
struct RandSpace{
long long seed;
long long Rand() {
return seed = ((seed*19260817) ^ 233333) & ((1<<24)-1);
}
void gen(int *A){
for(register int i = 1; i <= n; i++) A[i] = i;
for(register int i = 1; i <= n; i++) swap(A[i], A[Rand()%i + 1]);
}
}rs[3];
struct Person{
int a,b,c;
}pp[maxn],qq[maxn];
int qqpos = 0;
int tmpa[maxn];
inline bool strongCmp(const Person &a,const Person &b){
return (a.a < b.a) && (a.b < b.b) && (a.c < b.c);
}
ll ans = 0;
namespace solve30{
//暴力
void solve(){
ans = 0;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(strongCmp(pp[i],pp[j]) || strongCmp(pp[j],pp[i])) ans++;
}
}
printf("%lld\n",ans);
}
}
namespace solve70{
//CDQ分治三维偏序
int C[maxn];
int lowbit(int x){
return x & -x;
}
void update(int pos,int v){
for(int i=pos;i<=n;i+=lowbit(i)){
C[i] += v;
}
}
int query(int pos){
int ret = 0;
for(int i=pos;i>0;i-=lowbit(i)){
ret += C[i];
}
return ret;
}
inline bool cmp(const Person &a,const Person &b){
return a.a < b.a;
}
int ans = 0;
void CDQ_sort(int l,int r){
if(l == r) return;
int mid = (l + r) >> 1;
CDQ_sort(l,mid);
CDQ_sort(mid+1,r);
qqpos = 0;
int p1=l,p2=mid+1;
while(p1 <= mid || p2 <= r){
if(p1 <= mid && (pp[p1].b < pp[p2].b || p2 > r)){
qq[++qqpos] = pp[p1];
update(pp[p1].c,1);
++p1;
}else{
qq[++qqpos] = pp[p2];
ans += query(pp[p2].c-1);
++p2;
}
}
for(int i=l;i<=mid;i++) update(pp[i].c,-1);
for(int i=1;i<=qqpos;i++){
pp[l+i-1] = qq[i];
}
}
void solve(){
sort(pp+1,pp+1+n,cmp);
ans = 0;
CDQ_sort(1,n);
printf("%d\n",ans);
}
}
namespace solve100{
//正解 容斥
//S(i,j) = (pp[i].a > pp[j].a) + (pp[i].b > pp[j].b) + (pp[i].c > pp[j].c)
//M(i,j) = max{S(i,j) , S(j,i)}
//a: cnt whose [M(i,j) = 2]
//b: cnt whose [M(i,j) = 3]
//a + b = n * (n - 1) / 2;
//令 TT 为两两逆序对组数,即 (pp[i].a > pp[j].a && pp[i].b > pp[j].b) + (pp[i].a > pp[j].a && pp[i].c > pp[j].c) + (pp[i].c > pp[j].c && pp[i].b > pp[j].b)
//TT = 3*b + a
//TT - n * (n-1) / 2 = 2*b --> b = TT / 2 - n * (n-1) / 4
long long ans = 0;
long long TT = 0;
int C[maxn];
int lowbit(int x){
return x & -x;
}
void update(int pos,int v){
for(int i=pos;i<=n;i+=lowbit(i)){
C[i] += v;
}
}
int query(int pos){
int ret = 0;
for(int i=pos;i>0;i-=lowbit(i)){
ret += C[i];
}
return ret;
}
inline bool cmp1(const Person &a,const Person &b){
return a.a < b.a;
}
inline bool cmp2(const Person &a,const Person &b){
return a.b < b.b;
}
void solve(){
//a,b 逆序对
sort(pp+1,pp+1+n,cmp1);
for(int i=1;i<=n;i++){
if(i == 1){
update(pp[i].b,1);
continue;
}
TT += query(pp[i].b-1);
update(pp[i].b,1);
}
//a,c 逆序对
memset(C,0,sizeof C);
sort(pp+1,pp+1+n,cmp1);
for(int i=1;i<=n;i++){
if(i == 1){
update(pp[i].c,1);
continue;
}
TT += query(pp[i].c-1);
update(pp[i].c,1);
}
//b,c 逆序对
memset(C,0,sizeof C);
sort(pp+1,pp+1+n,cmp2);
for(int i=1;i<=n;i++){
if(i == 1){
update(pp[i].c,1);
continue;
}
TT += query(pp[i].c-1);
update(pp[i].c,1);
}
//cout << "TT:" << TT << endl;
//cout << "ans:" << TT / 2 - n * (n-1) / 4 << endl;
printf("%lld\n",TT / 2 - 1ll * n * (n-1) / 4);
}
}
int main(){
scanf("%d",&n);
for(register int i=0;i<3;i++){
scanf("%lld",&rs[i].seed);
}
rs[0].gen(tmpa);
for(register int i=1;i<=n;i++) pp[i].a = tmpa[i];
rs[1].gen(tmpa);
for(register int i=1;i<=n;i++) pp[i].b = tmpa[i];
rs[2].gen(tmpa);
for(register int i=1;i<=n;i++) pp[i].c = tmpa[i];
if(n <= 5000) solve30::solve();
else if(n <= 100010) solve70::solve();
else solve100::solve();
return 0;
}
CJ190725C
#include <bits/stdc++.h>
using namespace std;
int n,h;
struct node {
int a,b;
}W[4010];
bool cmp(node x,node y){
return x.a+x.b<y.a+y.b;
}
int dp[4010];
int main () {
scanf("%d",&n);
memset(dp,-1,sizeof(dp));
dp[0] = 0;
for (register int i = 1; i <= n; i++){
scanf("%d%d",&W[i].a,&W[i].b);
dp[0] += W[i].a;
}
scanf("%d",&h);
sort(W+1,W+n+1,cmp);
for (register int i = 1; i <= n; i++){
for (register int j = i; j >= 1; j--){
if (dp[j-1]+W[i].b>=h){
dp[j] = max(dp[j],dp[j-1]-W[i].a);
}
}
}
for (register int i = n; i >= 0; i--){
if (dp[i]>=0){
printf("%d",i);
return 0;
}
}
return 0;
}
CJ190723A
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
const int maxm = 2 * 100010;
struct edge{
int u,v,w;
}E2[maxm * 2];
int e2id;
int n,m,X;
inline bool cmp(const edge &a,const edge &b){
return a.w < b.w;
}
int fa[maxn],size[maxn];
void initfa(){
for(int i=0;i<=n;i++) fa[i] = i,size[i] = 1;
}
int get(int x){
if(fa[x] == x) return x;
return fa[x] = get(fa[x]);
}
long long ans = 0;
void merge(int x,int y,int isadd){
int fx = get(x),fy = get(y);
if(fx == fy) return;
fa[fx] = fy;
ans += size[fy] * size[fx] * isadd;
size[fy] += size[fx];
}
int main(){
scanf("%d%d%d",&n,&m,&X);
initfa();
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
if(w > X) continue;
E2[i].u = u;
E2[i].v = v;
E2[i].w = w;
if(w < X) {
merge(u,v,0);
}
}
for(int i=1;i<=m;i++){
if(E2[i].w == X){
merge(E2[i].u,E2[i].v,1);
}
}
printf("%lld\n",ans);
return 0;
}
CJ190723B
#include <bits/stdc++.h>
using namespace std;
const int maxn = 200010;
const int maxm = 2 * 200010;
int n,q;
struct edge{
int v,nxt;
}E[maxm * 2];
int p[maxn],eid;
void init(){
memset(p,-1,sizeof p);
eid = 0;
}
void insert(int u,int v){
E[eid].v = v;
E[eid].nxt = p[u];
p[u] = eid++;
}
void insert2(int u,int v){
insert(u,v);
insert(v,u);
}
int size[maxn],fa[maxn],hson[maxn],dep[maxn],anc[maxn][25];
void dfs1(int u){
//printf("u:%d\n",u);
size[u] = 1;
int msize = 0;
for(int i=p[u];~i;i=E[i].nxt){
int v = E[i].v;
if(fa[u] == v) continue;
dep[v] = dep[u] + 1;
fa[v] = u;
anc[v][0] = u;
dfs1(v);
size[u] += size[v];
if(size[v] > msize){
msize = size[v];
hson[u] = v;
}
}
}
int top[maxn];
void dfs2(int u,int tp){
top[u] = tp;
if(hson[u]){
dfs2(hson[u],tp);
}
for(int i=p[u];~i;i=E[i].nxt){
int v = E[i].v;
if(v == fa[u] || v == hson[u]) continue;
dfs2(v,v);
}
}
int lca(int x,int y){
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]]) swap(x,y);
x = fa[top[x]];
}
return dep[x] > dep[y] ? y : x;
}
void getanc(){
for(int i=1;(1 << i) <= n;i++){
for(int j=1;j<=n;j++){
anc[j][i] = anc[anc[j][i-1]][i-1];
}
}
}
int solve(int x,int y){
int tlca = lca(x,y);
if(tlca != x && tlca != y){
return size[x] + size[y];
}else{
if(dep[x] > dep[y]) swap(x,y);
int now = y;
for(int i=20;i>=0;i--){
if(dep[anc[now][i]] > dep[x]) now = anc[now][i];
}
return size[y] + n - size[now];
}
}
int main(){
init();
scanf("%d%d",&n,&q);
for(int i=1;i<=n-1;i++){
int u,v;
scanf("%d%d",&u,&v);
insert2(u,v);
}
dep[1] = 1;
dfs1(1);
dfs2(1,1);
getanc();
for(int i=1;i<=q;i++){
int u,v;
scanf("%d%d",&u,&v);
printf("%d\n",solve(u,v));
}
return 0;
}
CJ190723C
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3010,maxm = 4010;
const int inf = 0x3f3f3f3f;
typedef long long ll;
struct edge{
int v,w,nxt;
}E[maxm*2];
int p[maxn],eid;
void init(){
memset(p,-1,sizeof p);
eid = 0;
}
void insert(int u,int v,int w){
E[eid].v = v;
E[eid].w = w;
E[eid].nxt = p[u];
p[u] = eid++;
}
int n,m;
bool vis[maxn];
int dist[maxn][maxn];
void BFS(int u){
memset(vis,false,sizeof(vis));
queue<int> q;
q.push(u);
dist[u][u] = 0;
vis[u] = true;
while(!q.empty()){
int now = q.front();
q.pop();
for (int i=p[now];~i;i=E[i].nxt){
int v = E[i].v;
if (!vis[v]){
dist[u][v] = dist[u][now]+1;
vis[v] = true;
q.push(v);
}
}
}
}
int s1,t1,l1,s2,t2,l2;
int ans = 0;
int solve(int x,int y){
int res = 0x3f3f3f3f;
if (dist[x][y]+dist[x][s1]+dist[y][t1]<=l1&&dist[x][y]+dist[x][s2]+dist[y][t2]<=l2){
res = dist[x][y]+dist[x][s1]+dist[x][s2]+dist[y][t1]+dist[y][t2];
}
if (dist[x][y]+dist[x][s1]+dist[y][t1]<=l1&&dist[x][y]+dist[x][t2]+dist[y][s2]<=l2){
res = min(res,dist[x][y]+dist[x][s1]+dist[x][t2]+dist[y][s2]+dist[y][t1]);
}
return res;
}
int main(){
//freopen("buff.in","r",stdin);
//freopen("buff.out","w",stdout);
memset(dist,0x3f,sizeof dist);
init();
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
insert(u,v,1);
insert(v,u,1);
}
scanf("%d%d%d%d%d%d",&s1,&t1,&l1,&s2,&t2,&l2);
for(int i=1;i<=n;i++) BFS(i);
if(dist[s1][t1] > l1 || dist[s2][t2] > l2){
printf("That's all trouble!\n");
return 0;
}
ans = max(ans,m-dist[s1][t1]-dist[s2][t2]);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
ans = max(ans,m-solve(i,j));
}
}
printf("%d\n",ans);
return 0;
}
CJ190723D
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 100;
int n,m;
struct Ufs{
int fa[maxn],size[maxn];
void initfa(){
for(int i=1;i<=n;i++) fa[i] = i,size[i] = 1;
}
int get(int x){
while(fa[x] != x) x = fa[x];
return x;
}
int org_fa_s[maxn],fx_s[maxn],psta=0;
bool merge(int x,int y){
//printf("merge:%d %d\n",x,y);
int fx = get(x),fy = get(y);
if(fx == fy) {
psta++;
org_fa_s[psta] = fx_s[psta] = -1;
return false;
}
if(size[fx] > size[fy]) swap(fx,fy);
size[fy] += size[fx];
fx_s[++psta] = fx;
org_fa_s[psta] = fa[fx];
fa[fx] = fy;
return true;
}
bool undo(){
if(psta <= 0) return false;
int fx = fx_s[psta];
int org_fa = org_fa_s[psta--];
if(fx == -1 && org_fa == -1) return false;
int fy = fa[fx];
size[fy] -= size[fx];
fa[fx] = org_fa;
return true;
}
}ufs;
int ans[maxn],pans=0,ew[maxn],pew=0;
int nowcnt = 0,nowans = 0;
struct CMD{
char cmd;
int p1,p2;
}cmds[(int)5e5+100];
int main(){
scanf("%d%d",&n,&m);
ufs.initfa();
for(int i=1;i<=m;i++){
char cmd[10];
scanf("%s",cmd);
if(cmd[0] == 'A'){
cmds[i].cmd = 'A';
scanf("%d%d",&cmds[i].p1,&cmds[i].p2);
}else if(cmd[0] == 'D'){
cmds[i].cmd = 'D';
scanf("%d",&cmds[i].p1);
}else if(cmd[0] == 'R'){
cmds[i].cmd = 'R';
}
}
for(int i=1;i<=m;i++){
if(cmds[i].cmd == 'A'){
if(ufs.merge(cmds[i].p1,cmds[i].p2)){
nowans += i;
nowcnt++;
ew[++pew] = i;
}
if(nowcnt == n-1){
printf("%d\n",nowans);
ans[++pans] = nowans;
}else{
printf("0\n");
ans[++pans] = 0;
}
}else if(cmds[i].cmd == 'D'){
//printf("act:delete>>>\n");
int k = cmds[i].p1;
if(i+1<=m && cmds[i+1].cmd == 'R'){
//printf("delete>>>return:pans:%d k:%d\n",pans,k);
printf("%d\n",ans[pans-k]);
continue;
}else{
for(int j=1;j<=k;j++){
if(ufs.undo()){
nowcnt--;
nowans -= ew[pew--];
pans--;
}
}
if(nowcnt == n-1){
printf("%d\n",nowans);
}else{
printf("0\n");
}
}
}else{
if(cmds[i-1].cmd == 'A'){
if(ufs.undo()){
nowcnt--;
nowans -= ew[pew--];
}
if(nowcnt == n-1){
printf("%d\n",nowans);
}else{
printf("0\n");
}
}else{
if(nowcnt == n-1) printf("%d\n",nowans);
else printf("0\n");
}
}
}
return 0;
}
CJ190726A
#include <bits/stdc++.h>
using namespace std;
int t;
int n,q;
const int maxn = 25;
int a[maxn];
const int mod = 998244353;
namespace solve50{
inline int hcnt(int num){
register int ret = 0;
while(num){
ret += (num & 1);
num >>= 1;
}
return ret;
}
int totans = 0;
void solve(int id,int s,int t){
int curans = 0x3f3f3f3f;
int baks = s;
for(int i=0;i<(1<<n);i++){
//printf("i=%d\n",i);
int xorsum = 0;
int choicecnt = 0;
for(int j=0;j<n;j++){
if(i & (1 << j)){
xorsum ^= a[j+1];
//printf(">> xorsum ^= %d\n",a[j+1]);
choicecnt++;
}
}
//printf(">>pres:%d\n",s);
//printf(">>s=%d ^ %d\n",s,xorsum);
s = s ^ xorsum;
//printf(">>xorsum:%d s:%d\n",xorsum,s);
curans = min(curans,choicecnt + hcnt(t ^ s));
s = baks;
}
//printf("id:%d ans=%d\n",id,curans);
if(curans != 0x3f3f3f3f) totans += curans * id;
totans %= mod;
}
void init(){
totans = 0;
}
void out(){
printf("%d\n",totans);
}
}
namespace solveDP{
inline int hcnt(int num){
register int ret = 0;
while(num){
ret += (num & 1);
num >>= 1;
}
return ret;
}
long long ans = 0;
const int range = (1 << 18) + 1;
int dp[range];
void init(){
ans = 0;
memset(dp,0x3f,sizeof dp);
queue<int> q;
for(int i=1;i<=n;i++){
dp[a[i]] = 1;
q.push(a[i]);
}
while(!q.empty()){
int now = q.front();
q.pop();
for(int i=1;i<=n;i++){
if(dp[now] + 1 < dp[now ^ a[i]]){
dp[now ^ a[i]] = dp[now] + 1;
if((now ^ a[i] < range)) q.push(now ^ a[i]);
}
}
for(int bit=0;bit<=17;bit++){
if(dp[now] + 1 < dp[now ^ (1 << bit)]){
dp[now ^ (1 << bit)] = dp[now] + 1;
if((now ^ (1 << bit)) < range) q.push(now ^ (1 << bit));
}
}
}
}
void solve(int i,int s,int t){
ans += 1ll * i * min(hcnt(s ^ t),dp[s ^ t]) % mod;
//printf("id=%d ans:%d\n",i,min(hcnt(s ^ t),dp[s ^ t]));
ans %= mod;
}
void out(){
printf("%lld\n",ans);
}
}
int main(){
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++) scanf("%d",a+i);
if(n <= 3) solve50::init();
else solveDP::init();
for(int i=1;i<=q;i++){
int s,t;
scanf("%d%d",&s,&t);
if(n <= 3) solve50::solve(i,s,t);
else solveDP::solve(i,s,t);
}
if(n <= 3) solve50::out();
else solveDP::out();
}
return 0;
}
CJ190726B
// luogu-judger-enable-o2
#include <bits/stdc++.h>
using namespace std;
int len,tmp[26];
char mp[500010];
int pos[500010][26];
int dp[500010];
void dfs(int u,int len){
if (len==1){
for (register int i = 0; i < 26; i++){
if (pos[u][i]==1e9){
printf("%c",(char)('a'+i));
return;
}
}
}
for (register int i = 0; i < 26; i++){
if (dp[pos[u][i]]==len){
printf("%c",(char)('a'+i));
dfs(pos[u][i],len-1);
break;
}
}
}
int main () {
scanf("%s",mp+1);
len = strlen(mp+1);
for (register int i = 0; i < 26; i++)tmp[i] = 1e9;
for (register int i = len; i >= 1; i--){
memcpy(pos[i],tmp,sizeof(pos[i]));
tmp[mp[i]-'a'] = min(tmp[mp[i]-'a'],i);
}
memcpy(pos[0],tmp,sizeof(pos[0]));
for (register int i = len; i >= 1; i--){
dp[i] = 1e9;
for (register int j = 0; j < 26; j++){
if (pos[i][j]!=1e9){
dp[i] = min(dp[pos[i][j]]+1,dp[i]);
}else {
dp[i] = 2;
}
}
}
for (register int i = 0; i < 26; i++){
if (pos[0][i]==1e9){
printf("%c",(char)('a'+i));
return 0;
}
}
int ans = 1e9;
for (register int i = 0; i < 26; i++){
ans = min(ans,dp[tmp[i]]);
}
dfs(0,ans);
return 0;
}
CJ190726C
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e7 + 10;
int n,m;
const int mod = 1e9 + 7;
int phi[maxn],prime[maxn];
int cnt = 0;
bool vis[maxn];
void initPhi(){
phi[1] = 1;
vis[1] = true;
for(int i=2;i<maxn;i++){
if(!vis[i]){
prime[++cnt] = i;
phi[i] = i-1;
}
for(int j=1;j<=cnt;j++){
if(i * prime[j] >= maxn) break;
vis[i * prime[j]] = true;
if(i % prime[j] == 0){
phi[i * prime[j]] = phi[i] * prime[j];
break;
}else{
phi[i * prime[j]] = phi[i] * phi[prime[j]];
}
}
}
}
ll sum1 = 0,sum2 = 0,sum3 = 0;
ll inv2 = 500000004,inv6 = 166666668;
ll C(int _n,int _m){
return 1ull * _n * (_n - 1) % mod * (_n - 2) % mod * inv6 % mod;
}
void calcSum3(){
sum3 = (1ull * n * C(m,3) % mod + 1ull * m * C(n,3) % mod) % mod;
}
signed main(){
//freopen("c.in","r",stdin);
//freopen("c.out","w",stdout);
scanf("%d%d",&n,&m);
initPhi();
/*
for(int i=1;i<=100;i++){
printf("phi[%d]=%lld\n",i,phi[i]);
}
*/
calcSum3();
int lim = min(n-1,m-1);
for(int d=1;d<=lim;d++){
int t1 = (m-1) / d;
ll s1 = 1ull * (m - d + m - 1ull * t1 * d) * t1 % mod * inv2 % mod;
int t2 = (n-1) / d;
ll s2 = 1ull * (n - d + n - 1ull * t2 * d) * t2 % mod * inv2 % mod;
sum1 += 1ull * s1 * s2 % mod * phi[d] % mod;
sum1 %= mod;
//printf("res[d=%lld]=%lld\n",d,sum1);
}
int t1 = m-1;
ll s1 = ((1ull * m * t1 % mod - 1ull * (1 + t1) * t1 % mod * inv2 % mod) + mod) % mod;
int t2 = n-1;
ll s2 = ((1ull * n * t2 % mod - 1ull * (1 + t2) * t2 % mod * inv2 % mod) + mod) % mod;
s2 *= s1;
s2 %= mod;
sum2 = s2;
//printf("sum1=%lld sum2=%lld sum3=%lld\n",sum1,sum2,sum3);
printf("%llu\n",(sum3 + ((sum1-sum2)%mod+mod)%mod*2) % mod);
return 0;
}
Codechef REIGN
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 100100;
int T,n,k;
int a[maxn];
int premax[maxn],sufmax[maxn];
signed main(){
scanf("%lld",&T);
while(T--){
int ans = -1e18;
memset(premax,0,sizeof premax);
memset(sufmax,0,sizeof sufmax);
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++) scanf("%lld",a+i);
int nowsum = 0;
int nowmaxlen = 0;
premax[0] = -1e18;
for(int i=1;i<=n;i++){
nowsum += a[i];
if(nowsum < 0){
premax[i] = max(premax[i-1],nowsum);
nowsum = 0;
nowmaxlen = 0;
continue;
}
premax[i] = max(premax[i-1],nowsum);
++nowmaxlen;
}
nowsum = 0;
nowmaxlen = 0;
sufmax[n+1] = -1e18;
for(int i=n;i>=1;i--){
nowsum += a[i];
if(nowsum < 0){
sufmax[i] = max(sufmax[i+1],nowsum);
nowsum = 0;
nowmaxlen = 0;
continue;
}
sufmax[i] = max(sufmax[i+1],nowsum);
++nowmaxlen;
}
//for(int i=1;i<=n;i++) cout << "premax[" << i << "]=" << premax[i] << endl;
//cout << endl;
//for(int i=1;i<=n;i++) cout << "sufmax[" << i << "]=" << sufmax[i] << endl;
for(int i=1;i<=n-k-1;i++){
ans = max(ans,premax[i] + sufmax[i+k+1]);
}
printf("%lld\n",ans);
}
return 0;
}
CodeForces 1037E
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 100;
vector<int> G[maxn];
int uu[maxn],vv[maxn];
int degree[maxn];
int ans[maxn];
int nowans = 0;
int n,m,k;
bool del[maxn];
void BFS(int u){
queue<int> q;
q.push(u);
del[u] = true;
degree[u]--;
while(!q.empty()){
u = q.front();
q.pop();
nowans--;
for(int i=0;i<G[u].size();i++){
int v = G[u][i];
if(del[v]) continue;
degree[v]--;
if(degree[v] < k){
del[v] = true;
q.push(v);
}
}
}
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;i++){
scanf("%d%d",uu+i,vv+i);
G[uu[i]].push_back(vv[i]);
G[vv[i]].push_back(uu[i]);
degree[uu[i]]++;
degree[vv[i]]++;
}
nowans = n;
for(int i=1;i<=n;i++){
if(!del[i] && degree[i] < k){
BFS(i);
}
}
ans[m] = nowans;
for(int i=m;i>=1;i--){
if(!del[uu[i]]) degree[vv[i]]--;
if(!del[vv[i]]) degree[uu[i]]--;
G[uu[i]].pop_back();
G[vv[i]].pop_back();
if(!del[uu[i]] && degree[uu[i]] < k) BFS(uu[i]);
if(!del[vv[i]] && degree[vv[i]] < k) BFS(vv[i]);
ans[i-1] = nowans;
if(nowans == 0) break;
}
for(int i=1;i<=m;i++){
printf("%d\n",ans[i]);//
}//
return 0;
}
Codechef DIGJUMP
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
char str[maxn];
int len;
vector<int> buk[11];
int dis[maxn];
bool vis[maxn];
bool visbuk[11];
int BFS(int x){
queue<int> q;
q.push(x);
vis[x] = true;
while(!q.empty()){
int now = q.front();
//printf("now:%d\n",now);
q.pop();
if(now == len-1){
return dis[now];
}
if(now - 1 >= 0 && dis[now-1] > dis[now] + 1){
dis[now-1] = dis[now] + 1;
if(!vis[now-1]) {
vis[now-1] = true;
q.push(now-1);
}
}
if(now + 1 < len && dis[now + 1] > dis[now] + 1){
dis[now+1] = dis[now] + 1;
if(!vis[now+1]) {
vis[now+1] = true;
q.push(now+1);
}
}
if(visbuk[str[now]-'0']) continue;
for(int i=0;i<buk[str[now]-'0'].size();i++){
if(!vis[buk[str[now]-'0'][i]]) {
vis[buk[str[now]-'0'][i]] = true;
q.push(buk[str[now]-'0'][i]);
}
dis[buk[str[now]-'0'][i]] = min(dis[buk[str[now]-'0'][i]],dis[now]+1);
}
visbuk[str[now]-'0'] = true;
}
return -23333;
}
int main(){
scanf("%s",str);
memset(dis,0x3f,sizeof dis);
len = strlen(str);
for(int i=0;i<len;i++){
buk[str[i] - '0'].push_back(i);
}
dis[0] = 1;
int ans = BFS(0);
printf("%d\n",ans-1);
return 0;
}
Codechef PERMPART
#include <bits/stdc++.h>
using namespace std;
int T,n;
const int maxn = 1000100;
int a[maxn],buk[maxn*2];
signed main(){
scanf("%d",&T);
while(T--){
int ans1 = 0;
scanf("%d",&n);
memset(buk,0,sizeof buk);
int maxa = 0;
for(int i=1;i<=n;i++){
scanf("%d",a+i);
if(a[i] > 2 * n){
ans1++;
}else{
buk[a[i]]++;
maxa = max(maxa,a[i]);
}
}
priority_queue<int,vector<int>,greater<int> > pq;
int ans2 = 0;
for(int i=1;i<=maxa;i++){
pq.push(buk[i]);
if(!pq.empty() && pq.top() < buk[i]){
ans2 += buk[i] - pq.top();
pq.pop();
pq.push(buk[i]);
}
}
printf("%d\n",ans1+ans2);
}
return 0;
}
ZJOI2007 仓库建设
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1000100;
int n;
ll X[maxn],P[maxn],C[maxn];
ll F[maxn],T[maxn];
ll dp[maxn];
ll M(int x){
return dp[x] + T[x];
}
int q[maxn * 2];
void doDP(){
int head=0,tail=-1;
q[++tail] = 0;
dp[0] = 0;
for(int i=1;i<=n;i++){
while(head < tail){
int k1 = q[head],k2 = q[head+1];
if(1.0*(M(k1) - M(k2))/(F[k1] - F[k2]) <= X[i]){
head++;
}else{
break;
}
}
int bestk = q[head];
dp[i] = C[i] + dp[bestk] + X[i] * (F[i-1] - F[bestk]) - (T[i-1] - T[bestk]);
//printf("dp[%d]=%lld\n",i,dp[i]);
while(head < tail){
int k1 = q[tail],k2 = q[tail-1];
double kk1 = 1.0 * (M(k1) - M(k2)) / (F[k1] - F[k2]);
double kk2 = 1.0 * (M(i) - M(k1)) / (F[i] - F[k1]);
if(kk2 <= kk1){
tail--;
}else{
break;
}
}
q[++tail] = i;
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld%lld%lld",&X[i],&P[i],&C[i]);
F[i] = F[i-1] + P[i];
T[i] = T[i-1] + P[i] * X[i];
}
doDP();
printf("%lld\n",dp[n]);
return 0;
}
BZOJ1303
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int n,b;
int a[maxn];
int tmp[maxn];
int ans = 0;
int posb = -1;
map<int,int> mp;
int main(){
scanf("%d%d",&n,&b);
for(int i=1;i<=n;i++) scanf("%d",a+i);
for(int i=1;i<=n;i++){
if(a[i] < b) tmp[i] = -1;
else if(a[i] == b) tmp[i] = 0,posb = i;
else if(a[i] > b) tmp[i] = 1;
}
int presum = 0;
for(int i=posb-1;i>=1;i--){
presum += tmp[i];
if(presum == 0) ans++;
mp[presum]++;
}
presum = 0;
for(int i=posb+1;i<=n;i++){
presum += tmp[i];
if(presum == 0) ans++;
if(mp.count(-presum)) ans += mp[-presum];
}
printf("%d\n",ans+1);
return 0;
}
BZOJ2783
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
struct edge{
int v,nxt;
}E[maxn*2];
int p[maxn],eid;
void init(){
memset(p,-1,sizeof p);
eid = 0;
}
void insert(int u,int v){
E[eid].v = v;
E[eid].nxt = p[u];
p[u] = eid++;
}
void insert2(int u,int v){
insert(u,v);
insert(v,u);
}
int n,s;
int root;
int pw[maxn];
int x[maxn],y[maxn];
int in[maxn];
int dis[maxn],dep[maxn],anc[maxn][21];
void dfs(int u){
for(int i=p[u];~i;i=E[i].nxt){
int v = E[i].v;
if(v == anc[u][0]) continue;
anc[v][0] = u;
dep[v] = dep[u] + 1;
dis[v] = dis[u] + pw[v];
dfs(v);
}
}
void calcAnc(){
for(int i=1;i<=20;i++){
for(int j=1;j<=n;j++){
anc[j][i] = anc[anc[j][i-1]][i-1];
}
}
}
int ans = 0;
void solve(int po){
//printf("solve %d\n",po);
if(po == root){
ans += (pw[po] == s);
//printf("ans += %d\n",(int)(pw[po] == s));
return;
}
int cur = po;
for(int i=20;i>=0;i--){
int ancc = anc[cur][i];
int llen = dis[po] - dis[anc[ancc][0]];
if(llen <= s) cur = ancc;
}
//printf("cur:%d\n",cur);
int llenn = dis[po] - dis[anc[cur][0]];
//printf("llenn:%d\n",llenn);
ans += (llenn == s);
//printf("ans += %d\n",(int)(llenn == s));
}
int main(){
init();
scanf("%d%d",&n,&s);
for(int i=1;i<=n;i++) scanf("%d",pw+i);
for(int i=1;i<=n-1;i++){
scanf("%d%d",x+i,y+i);
insert2(x[i],y[i]);
in[y[i]]++;
}
for(int i=1;i<=n;i++)
if(!in[i]){
root = i;
break;
}
dis[root] = pw[root];
dep[root] = 0;
dfs(root);
anc[root][0] = n+1;
pw[n+1] = 0;
calcAnc();
for(int i=1;i<=n;i++){
solve(i);
}
printf("%d\n",ans);
return 0;
}
BZOJ3631
#include <bits/stdc++.h>
using namespace std;
const int maxn = 300010;
const int maxm = maxn * 2;
struct edge{
int v,nxt;
}E[maxm];
int p[maxn],eid;
void init(){
memset(p,-1,sizeof p);
eid = 0;
}
void insert(int u,int v){
E[eid].v = v;
E[eid].nxt = p[u];
p[u] = eid++;
}
void insert2(int u,int v){
insert(u,v);
insert(v,u);
}
int n;
int a[maxn];
int size[maxn],hson[maxn],dep[maxn],fa[maxn];
void dfs1(int u){
int maxsz = 0;
size[u] = 1;
for(int i=p[u];~i;i=E[i].nxt){
int v = E[i].v;
if(fa[u] == v) continue;
fa[v] = u;
dep[v] = dep[u] + 1;
dfs1(v);
size[u] += size[v];
if(size[v] > maxsz){
hson[u] = v;
maxsz = size[v];
}
}
}
int top[maxn];
void dfs2(int u,int tp){
top[u] = tp;
if(hson[u]){
dfs2(hson[u],tp);
}
for(int i=p[u];~i;i=E[i].nxt){
int v = E[i].v;
if(v == fa[u]) continue;
if(v == hson[u]) continue;
dfs2(v,v);
}
}
int lca(int xx,int yy){
while(top[xx] != top[yy]){
if(dep[top[xx]] < dep[top[yy]]) swap(xx,yy);
xx = fa[top[xx]];
}
return dep[xx] < dep[yy] ? xx : yy;
}
int dt[maxn];
void modify(int x,int y,int v){
int cc = lca(x,y);
int facc = fa[cc];
dt[x] += v;
dt[y] += v;
dt[facc] -= v;
dt[cc] -= v;
}
void dfs3(int u){
for(int i=p[u];~i;i=E[i].nxt){
int v = E[i].v;
if(v == fa[u]) continue;
dfs3(v);
dt[u] += dt[v];
}
}
int main(){
init();
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",a+i);
for(int i=1;i<=n-1;i++){
int u,v;
scanf("%d%d",&u,&v);
insert2(u,v);
}
dep[1] = 1;
dfs1(1);
dfs2(1,1);
modify(a[1],a[1],1);
for(int i=2;i<=n;i++){
modify(a[i-1],a[i-1],-1);
modify(a[i-1],a[i],1);
}
modify(a[n],a[n],-1);
dfs3(1);
for(int i=1;i<=n;i++){
printf("%d\n",dt[i]);
}
return 0;
}
P1083 借教室
// luogu-judger-enable-o2
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000010;
struct node{
int lazy;
int minv;
}T[maxn<<2];
int n,m;
int R[maxn];
void pushup(int id){
T[id].minv = min(T[id<<1].minv,T[id<<1|1].minv);
}
void pushdown(int id){
if(T[id].lazy){
T[id<<1].lazy += T[id].lazy;
T[id<<1|1].lazy += T[id].lazy;
T[id<<1].minv += T[id].lazy;
T[id<<1|1].minv += T[id].lazy;
T[id].lazy = 0;
}
}
void build(int id,int l,int r){
if(l == r){
T[id].minv = R[l];
return;
}
int mid = l + r >> 1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
pushup(id);
}
void update(int id,int l,int r,int x,int y,int v){
if(x <= l && r <= y){
T[id].lazy += v;
T[id].minv += v;
return;
}
pushdown(id);
int mid = l + r >> 1;
if(x <= mid){
update(id<<1,l,mid,x,y,v);
}
if(y > mid){
update(id<<1|1,mid+1,r,x,y,v);
}
pushup(id);
}
int query(int id,int l,int r,int x,int y){
if(x <= l && r <= y){
return T[id].minv;
}
pushdown(id);
int mid = l + r >> 1;
int ret = 0x3f3f3f3f;
if(x <= mid){
ret = min(ret,query(id<<1,l,mid,x,y));
}
if(y > mid){
ret = min(ret,query(id<<1|1,mid+1,r,x,y));
}
return ret;
}
vector<int> cant;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",R+i);
}
build(1,1,n);
for(int i=1;i<=m;i++){
int d,s,t;
scanf("%d%d%d",&d,&s,&t);
int minv = query(1,1,n,s,t);
if(minv < d){
cant.push_back(i);
}else{
update(1,1,n,s,t,-d);
}
}
if(cant.size()==0){
printf("0\n");
return 0;
}
printf("-1\n");
printf("%d\n",cant[0]);
return 0;
}
P2161 [SHOI2009]会场预约
标程用词不雅,请不要学习!
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 500010;
const int fuckyou = 500000;
struct node{
int lv,rv;
int cnt;
int lazy;
node(){
lazy = -1;
cnt = 0;
}
}T[maxn<<2];
void pushdown(int id){
if(T[id].lazy != -1){
//printf("pushdown\n");
T[id<<1].lazy = T[id].lazy;
T[id<<1|1].lazy = T[id].lazy;
if(T[id].lazy) T[id<<1].cnt = 1;
else T[id<<1].cnt = 0;
if(T[id].lazy) T[id<<1|1].cnt = 1;
else T[id<<1|1].cnt = 0;
T[id<<1].lv = T[id<<1].rv = T[id].lazy;
T[id<<1|1].lv = T[id<<1|1].rv = T[id].lazy;
T[id].lazy = -1;
}
}
node merge(const node &a,const node &b){
node ret;
ret.lv = a.lv;
ret.rv = b.rv;
ret.cnt = a.cnt + b.cnt;
//printf("a.cnt=%d b.cnt=%d a.rv = %d b.lv = %d\n",a.cnt,b.cnt,a.rv,b.lv);
if(a.rv == b.lv && a.rv != 0) {
ret.cnt--;
//cout << "cnt--" << endl;
}
ret.cnt = max(ret.cnt,0ll);
//cout << "ret.cnt=" << ret.cnt << endl;
return ret;
}
void pushup(int id){
T[id] = merge(T[id<<1],T[id<<1|1]);
}
node query(int id,int l,int r,int x,int y){
//printf("query l=%d r=%d\n",l,r);
if(x <= l && r <= y){
return T[id];
}
pushdown(id);
int mid = l + r >> 1;
node ret;
ret.cnt = -999;
if(x <= mid){
ret = query(id<<1,l,mid,x,y);
}
if(y > mid){
if(ret.cnt != -999) ret = merge(ret,query(id<<1|1,mid+1,r,x,y));
else ret = query(id<<1|1,mid+1,r,x,y);
}
return ret;
}
void update(int id,int l,int r,int x,int y,int v){
if(x <= l && r <= y){
T[id].lazy = v;
if(v != 0) T[id].cnt = 1;
else T[id].cnt = 0;
T[id].lv = T[id].rv = v;
return;
}
pushdown(id);
int mid = l + r >> 1;
if(x <= mid){
update(id<<1,l,mid,x,y,v);
}
if(y > mid){
update(id<<1|1,mid+1,r,x,y,v);
}
pushup(id);
}
int n;
int now = 0;
int L[maxn],R[maxn];
void fuckyoumother(int id,int l,int r){
L[id] = l,R[id] = r;
int jian = query(1,1,fuckyou,l,r).cnt;
now -= jian;
now++;
cout << jian << '\n';
int sl = L[query(1,1,fuckyou,l,l).lv];
int sr = R[query(1,1,fuckyou,r,r).lv];
if(sl == 0) sl = l;
if(sr == 0) sr = r;
update(1,1,fuckyou,sl,sr,0);
update(1,1,fuckyou,l,r,id);
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
for(int i=1;i<=n;i++){
char cc;
cin >> cc;
if(cc == 'A'){
int l,r;
cin >> l >> r;
fuckyoumother(i,l,r);
}else{
cout << now << '\n';
}
//cout << "fuckyou" << endl;
}
return 0;
}
P1880 [NOI1995]石子合并
#include <bits/stdc++.h>
using namespace std;
const int maxn = 110*2;
int n;
int a[maxn],prea[maxn];
long long mindp[maxn][maxn],maxdp[maxn][maxn];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",a+i);
a[i+n] = a[i];
}
for(int i=1;i<=2*n;i++) prea[i] = prea[i-1] + a[i];
memset(mindp,0,sizeof mindp);
memset(maxdp,0,sizeof maxdp);
for(int len=1;len<2*n;len++){
for(int i=1,j=i+len;i<=2*n && j<=2*n;i++,j=i+len){
mindp[i][j] = 1e18;
for(int k=i;k<j;k++){
mindp[i][j] = min(mindp[i][j],mindp[i][k] + mindp[k+1][j] + prea[j] - prea[i-1]);
//printf("mindp[%d][%d]=%lld\n",i,j,mindp[i][j]);
//printf("prea[%d] - prea[%d]=%d\n",j,i-1,prea[j] - prea[i-1]);
maxdp[i][j] = max(maxdp[i][j],maxdp[i][k] + maxdp[k+1][j] + prea[j] - prea[i-1]);
}
}
}
long long minans = 0x3f3f3f3f;
long long maxans = 0;
for(int i=1;i<=n;i++){
//cout << ">>" << mindp[i][i+n-1] << endl;
minans = min(minans,mindp[i][i+n-1]);
maxans = max(maxans,maxdp[i][i+n-1]);
}
printf("%lld\n%lld",minans,maxans);
return 0;
}
计蒜客-银河战舰
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
const double pi = acos(-1);
struct Matrix{
int n,m;
double mat[6][6];
Matrix(){}
Matrix(int _n,int _m){
n = _n;
m = _m;
memset(mat,0,sizeof mat);
}
Matrix(int _n,int _m,char unit){
n = _n;
m = _m;
memset(mat,0,sizeof mat);
for(int i=0;i<3;i++){
mat[i][i] = 1;
}
}
double* operator [] (const int x) {
return mat[x];
}
Matrix operator * (Matrix &b) const{
Matrix res(3,3);
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
for(int k=0;k<3;k++){
res[i][j] += mat[i][k] * b[k][j];
}
}
}
return res;
}
void operator *= (Matrix b) {
*this = *this * b;
}
};
struct node{
Matrix lazymul;
node(){
lazymul = Matrix(3,3,'E');
}
int l,r;
}T[maxn << 2];
void build(int id,int l,int r){
T[id].l = l;
T[id].r = r;
if(l == r){
return;
}
int mid = l + r >> 1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
}
void pushdown(int id){
T[id<<1].lazymul *= T[id].lazymul;
T[id<<1|1].lazymul *= T[id].lazymul;
T[id].lazymul = Matrix(3,3,'E');
}
void updateMul(int id,int l,int r,int x,int y,Matrix &val){
if(x <= l && r <= y){
T[id].lazymul *= val;
return;
}
int mid = l + r >> 1;
pushdown(id);
if(x <= mid){
updateMul(id<<1,l,mid,x,y,val);
}
if(y > mid){
updateMul(id<<1|1,mid+1,r,x,y,val);
}
}
Matrix* query(int id,int l,int r,int x){
if(l == r){
return &T[id].lazymul;
}
pushdown(id);
int mid = l + r >> 1;
if(x <= mid){
return query(id<<1,l,mid,x);
}else{
return query(id<<1|1,mid+1,r,x);
}
}
int n,m;
double xx[maxn],yy[maxn];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lf%lf",&xx[i],&yy[i]);
}
build(1,1,n);
scanf("%d",&m);
for(int i=1;i<=m;i++){
char cc;
scanf(" %c",&cc);
if(cc == 'M'){
double l,r,p,q;
scanf("%lf%lf%lf%lf",&l,&r,&p,&q);
Matrix mat(3,3);
mat[0][0] = 1;
mat[1][1] = 1;
mat[2][0] = p;
mat[2][1] = q;
mat[2][2] = 1;
updateMul(1,1,n,l,r,mat);
}else if(cc == 'X'){
int l,r;
scanf("%d%d",&l,&r);
Matrix mat(3,3);
mat[0][0] = 1;
mat[1][1] = -1;
mat[2][2] = 1;
updateMul(1,1,n,l,r,mat);
}else if(cc == 'Y'){
int l,r;
scanf("%d%d",&l,&r);
Matrix mat(3,3);
mat[0][0] = -1;
mat[1][1] = 1;
mat[2][2] = 1;
updateMul(1,1,n,l,r,mat);
}else if(cc == 'O'){
int l,r;
scanf("%d%d",&l,&r);
Matrix mat(3,3);
mat[0][1] = 1;
mat[1][0] = 1;
mat[2][2] = 1;
updateMul(1,1,n,l,r,mat);
}else if(cc == 'R'){
int l,r;
double a;
scanf("%d%d%lf",&l,&r,&a);
a = 1.0 * a * pi / 180;
Matrix mat(3,3);
mat[0][0] = cos(a);
mat[0][1] = sin(a);
mat[1][0] = -sin(a);
mat[1][1] = cos(a);
mat[2][2] = 1;
updateMul(1,1,n,l,r,mat);
}
}
for(int i=1;i<=n;i++){
Matrix qr = *query(1,1,n,i);
Matrix org(3,3);
org[0][0] = xx[i],org[0][1] = yy[i],org[0][2] = 1;
Matrix ans = org * qr;
printf("%.2lf %.2lf\n",ans[0][0],ans[0][1]);
}
return 0;
}
P3294 [SCOI2016]背单词
标程取名有点丑,简单解释一下
dfs1就是从原trie树中建立新的图,st[]就是题解所提及的栈
dfs1_就是维护新图的newsz[],即新图子树大小,并把计算结果放入优先队列son[]
dfs2就是按照newsz[]从小到大的顺序dfs,val[]记录的就是给每个终端节点分配的编号
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 500010;
int ch[maxn][26],id=0;
int cnt[maxn];
int sz[maxn];
ll ans = 0;
void insert(char *s){
int len = strlen(s);
int p = 0;
for(int i=len-1;i>=0;i--){
if(ch[p][s[i]-'a']){
p = ch[p][s[i]-'a'];
}else{
ch[p][s[i]-'a'] = ++id;
p = ch[p][s[i]-'a'];
}
sz[p]++;
}
cnt[p]++;
}
int n;
char str[100010];
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > son[maxn];
vector<int> sons[maxn];
int newsz[maxn];
int st[maxn],pst=0;
void dfs1(int u){
if(cnt[u]){
sons[st[pst]].push_back(u);
st[++pst] = u;
}
for(int i=0;i<26;i++){
int v = ch[u][i];
if(v){
dfs1(v);
}
}
if(cnt[u]){
--pst;
}
}
void dfs1_(int u){
newsz[u] = 1;
for(int i=0;i<sons[u].size();i++){
int v = sons[u][i];
dfs1_(v);
son[u].push(make_pair(newsz[v],v));
//printf("%d.push(%d,%d)\n",u,newsz[v],v);
newsz[u] += newsz[v];
}
}
int val[maxn];
int nowstep = 0;
void dfs2(int u){
//cout << "u:" << u << endl;
if(cnt[u]){
val[u] = ++nowstep;
//printf("val[%d]=%d\n",u,val[u]);
ans += val[u] - st[pst];
st[++pst] = val[u];
}
while(!son[u].empty()){
pair<int,int> now = son[u].top();
//printf("now:%d,%d\n",now.first,now.second);
son[u].pop();
dfs2(now.second);
}
if(cnt[u]){
pst--;
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",str);
insert(str);
}
dfs1(0);
dfs1_(0);
dfs2(0);
printf("%lld\n",ans);
return 0;
}
这事ao的
By 某韦 at March 17th, 2020 at 10:56 pm.