2019集训队作业做题实况[1](1-30):
50-1(19.10.23)(树的性质):
https://codeforces.com/contest/516/problem/D
https://codeforces.com/contest/516/submission/63242051
大意:
给一棵树,定义\(d[x]=max(dis(x,y))\)。
求一个联通块\(S\),使得\(max(d[x∈S])-min(d[y∈S])<=l\),求\(max(|S|)\)。
\(d[x]\)肯定是\(x\)到直径两个端点的最大值。
然后发现可以直接lct维护联通块大小。
发现性质,若以d最小的为根建树,\(d[fa[x]]<=d[x]\)。
那么就是扫描线,对于x,可以用树状数组直接统计子树里\([d[y]<=d[x]+l]\),因为连续。
上网发现还可以直接用并查集维护联通块大小,删掉时直接减1,并查集不用变,还是因为连续。
50-3(19.10.24)(计数+分治):
https://atcoder.jp/contests/agc023/tasks/agc023_e
https://atcoder.jp/contests/agc023/submissions/8106222
大意:
求满足p[i]<=a[i],p是一个排列,p的逆序对数和。
考虑求p的方案数。
从大到小给每个数选择位置,这样的话就可以确定这个数可以放的位置数。
设\(cnt[i]=\sum [a[j]>=i]\),\(Ans=\prod cnt[i]-(n-i)\)
要考虑逆序对数的话,枚举两个\(i,j(i<j)\),假设\(a[i]<=a[j]\)
当\(p[j]>a[i]\)时,不会产生逆序对数,将\(a[j]\)调整至\(a[i]\),发现恰好有一半的排列就是\(p[i]>p[j]\)的。
若\(a[i]>=a[j]\),考虑用总数-顺序对数,顺序对数和上面一样,把\(a[i]\)调整至\(a[j]\)
若调整\(a[i]<=a[j]\),则\(cnt[x]--,x∈(a[i],a[j]]\),对合法排列的影响可以预处理\({cnt[i]-n+i-1\over cnt[i]-n+i}\)的区间积来实现目的。
这样得到了\(O(n^2)\)的做法。
用线段树分治来统计答案就是\(O(n~log~n)\)的。
50-2(19.10.24)(概率+积分):
https://atcoder.jp/contests/agc032/tasks/agc032_f
https://atcoder.jp/contests/agc032/submissions/8109887
大意:
一个圆被随机\(n\)条直径分割,设\(s\)为一个扇形的面积,求\(min(|s-1/3|)\)。
第一步需要一个不可能想到的转换考虑把一条线定位0°,然后划分成三个区域,0-120°,120-240°,240°-360°
这个区域里的划分看作三种颜色红绿蓝,然后全部\(mod~120°\)搞到\(0-120°\)里,发现问题变为\(0-120°\)里,随机\(n-1\)条划分,颜色也随机,求颜色不同的划分的最近距离,注意0°视作有一条红色的,120°视作有一条蓝色的。
\(n-1\)条弧把1/3的区域划分成了\(n\)段。
设\(E(i)\)表示长度为1的线段分成\(n\)段第\(i\)长段的期望长度。
通过推理可以得到:
$Ans=
\(Ans=\sum_{i=1}^n 3^{i-1}*(E(i)-E(i-1))*{1\over 3}\)。
即一条线段两个点同色的概率是\({1 \over 3}\),要使\(E(i)-E(i-1)\)被统计到,则\(i\)以前的线段都要同色,所以是\(3^{i-1}\),长度上限是\({1\over3}\),还要乘\({1\over 3}\)。
问题在于求\(E(i)\)。
先求\(E(1)\),设\(P(i)\)表示长度为1的线段分成\(n\)段最短线段长度\(>=i\)的概率。
\(E(1)=\int_{x=0}^{1/n} P(x) ~dx\)
\(P(x)=(1-nx)^{n-1}\),下面将解释这个东西:
考虑把线段分为\(a(a->∞)\)段,在其中随机选\(n-1\)个点就分成了\(n\)段,方案数是\(a^{n-1}\)。
现在要求每一段线段的长度都\(>=x\),可以理解成少了\(nx*a\)个点,还剩\(a-nx*a\)个点,方案数是\((a-anx)^{n-1}\)。
则概率就是\((a-anx)^{n-1}/a^{n-1}=(1-nx)^{n-1}\)。
\(\int_{x=0}^{1/n} (1-nx)^{n-1}~dx\)
\(=\int_{x=0}^{1/n} x^{n-1} ~ dx\)
\(=1/n^2\)
所以\(E(1)={1 \over n^2}\)
然后推\(E(2)-E(1)\)。
这个就比较简单了,\(E(2)\)相当于还剩下\(1-E(1)*n\)的长度,分给\(n-1\)段,最短期望长度。
那么就是\(E(2)=(1-{E(1)*n})/(n-1)^2=n*(n-1)\)。
通过数归可以得到:\(E(k)-E(k-1)={1\over n*(n-k+1)}\)
那么这题的答案就是:\(Ans=\sum_{i=1}^n {1 \over 3^in(n-k+1)}\)。
49-1(19.10.26)(决策+贪心):
https://codeforces.com/contest/506/problem/C
https://codeforces.com/contest/506/submission/63412933
https://codeforces.com/contest/506/submission/63421859
大意:
有n个竹子,第i个竹子长度为h[i],每天的结束会长高a[i]
现在有m天,每一天可以做k次操作,每次操作可以选择一个竹子砍掉p,即高度h[i]=max(h[i]-p,0)
你需要最小化m天结束后最高的竹子的高度
n<=100000,m<=5000,k<=10
首先二分答案x。
solution1:
然后发现直接顺着搞无法决策每次机会给谁。
于是逆着搞,问题等价于,一开始每根竹子的高度是x,每次先降低a[i],操作可以拔高p,要求每根竹子任何时候高度非负,且最后的高度>=h[i]。
那么这个问题十分简单,主要是没了max(0,h-p)这种不好考虑得东西,先把所有操作留着,当一个竹子要h<0时,就给他加p,最后再填到h[i],看操作够不够用就行了。
要用\(priority\)_\(queue\),\(set\)被卡常。
solution2:
每根竹子至少要\(c[i]=\lceil {x + a[i]* m - h[i] \over p}\rceil\),事实也不会要更多次。
假设要多了一次,不如不做前面的那次不满p的,效果是一样的。
然后设\(d[i][j]\)表示第i根竹子的第j次操作至少要在\(d[i][j]\)天后。
\(d[i][j]\)怎么求是个好问题。
先思考\(c[i]\)刀要满足什么才能合法,就是它们的效果和\(>=x+a[i]*m-h[i]\)
也就是\(>=(c[i]-1)*p+(x+a[i]*m-h[i])~mod~p\)
则对于任意前\(j\)刀,砍的时候要满足:
\(h[i]+(d[i][j]-1)*a[i]>=(j-1)*p+(x-h[i])~mod~p\)
搞一下\(d[i][j]\)就出来了。
接着扫一遍,看看够不够用就好了。
1-2(19.10.26)(性质+线段树):
https://codeforces.com/contest/674/problem/G
https://codeforces.com/contest/674/submission/63429991
大意:
给出n个数,每次可以对一个区间进行整体赋值,或者询问一个区间频率\(>=p\%(20<=p<=100)\)的数。
在编程之美上看过这样一个问题:
有n个单词,其中有k个单词频率\(>={1 \over k +1}\),其它的都小于\({1\over k +1}\),要求利用\(O(k)\)的空间找出这k个单词。
考虑k=1的时候,只需要记录一个单词和一个计数器,如果新的单词和记录的一样,计数器+1,否则-1,当计数器=0时,记录的单词变为新的单词。
\(k>1\),类似的,就记k个不同的单词和计数器,加入一个新的,有相同的就把计数器+1,否则计数器全部-1,有计数器=0的就替换。
证明就是把上面的看做每次找k+1个不同的单词删掉,最后剩下的一定是那k个单词。
对于这道题,可以看做做\(k=\lfloor100/p\rfloor\),用线段树维护,\(O(k^2)\)暴力合并两个表即可。
时间复杂度:\(O(n~log~n~*k^2)\)
49-2(19.10.31)(势能分析,线段树)
https://codeforces.com/contest/679/problem/E
https://codeforces.com/contest/679/submission/63884902
大意:
有一个序列,每次可以区间赋值,或者区间加,如果区间加完后这个区间有42的次幂,那就继续求,还有询问一个的值。
42的次幂在int范围里的只有6个。
我们对每一个数设一个\(dis[i]\)表示\(a[i]\)到下一个42的幂的距离。
区间加法相当于dis区间减法,当减到<=0时,就顺便判一下并改一下,如果没有2操作的话,因为一个数最多被搞6次,复杂度就是:
\(O(n~log~n*6)\)。
考虑有了2操作,如果一个区间实际值一样,且dis<=0,那么一起修改,容易证明,复杂度还是:
\(O(n~log~n*6)\)。
48-1(19.10.31)(tarjan缩强联通分量)
https://codeforces.com/contest/555/problem/E
https://codeforces.com/contest/555/submission/63894938
太水了不讲了。
49-3(19.10.31)(转换模型+贪心+树形dp)
https://atcoder.jp/contests/arc098/tasks/arc098_d
https://atcoder.jp/contests/arc098/submissions/8223881
大意:
给出一个联通无向图,走到一个点的时候至少要有\(a[i]\)的金币,可以花下\(b[i]\)的金币买下这个点,求最少要多少金币才能把所有点买一遍。
我只能想到先选\(a[i]-b[i]\)大的比较优,但是这只能用于完全图的情况,然后就不会了。
事实上可以这么转换问题,就变得明了。
设\(c[i]=max(0,a[i]-b[i])\),要保证只要在这个点上,\(金币数>=c[i]\)。
这个问题,不难想到把\(c[i]\)最大的提出来,越早买它越好。
但是它的邻节点可以分成多个联通块,如果一开始就买了,可能就不能走到了其它地方了。
所以一定留在最后一个联通块进去前买。
记\(sumb[i]\)表示i为根的来联通块的\(\sum b\)
记\(g[i]\)表示i为根的联通块所需的金币数\(-sumb[i]\)。
考虑\(g[x]\)怎么转移,直接枚举最后走到的联通块y,因为\(c[x]\)是子联通块里最大的,所以一定能不用额外的金币通过其它子树,对于这个联通块所需的额外金币是\(max(g[y],c[y]-sumb[y])\),取所有y的最小值即可。
48-2(19.10.31)(辣鸡结论题):
https://atcoder.jp/contests/agc032/tasks/agc032_e
https://atcoder.jp/contests/agc032/submissions/8226893
大意:
把2n个数分成n对,使得\(max((a[i]+a[j]) ~mod ~m)\)最小化。
大胆猜想可以找到一个分界点,使得左边第一个和最后一个,第二个和倒数第二个……右边也是如此
这样会最优,证明可以看题解那6个图,然后用不等式做做发现就是对的。
题解:https://img.atcoder.jp/agc032/editorial.pdf
于是二分这个分界点就好了。
48-3(19.10.31)(博弈+最优化决策):
https://atcoder.jp/contests/agc032
https://atcoder.jp/contests/agc023/submissions/8229211
大意:
数轴上\(n\)个点,第i个点是\(x[i]\),人数为\(p[i]\)。
一开始所有人在车上,车在\(S\)上,每次进行投票,往正或者往负走,到达一个\(x[i]\)时,\(x[i]\)上的\(p[i]\)人会下来。
每个人秃顶聪明,会希望自己在车上的时间最小,输出最后下的人的时间。
*想题两小时,做题五分钟——论atcoder做题感受。
很不自然地考虑\(1\)和\(n\)两个地方的人的决策。
若\(p[1]>=p[n]\),即使\(n\)上面的人把车往右边拉,走到了\(x[n-1]\),由于\(p[1]>=p[n]\),车还是往回走。
也就是\(n\)一定在\(1\)的后面,\(T(n)=T(1)+x[n]-x[1]\),所以\(n\)上面的人不如让\(T(1)\)最小,也就是跟着\(1\)决策。
\(p[1]+=p[n]\),现在变成了\([1-n-1]\),求\(T(1)\)的子问题,递归求解。
\(p[1]<p[n]\)的情况同理。
直到最后只剩一边的人,那么就不用决策了,直接走。
43-1(19.10.31)(分段矩阵乘法):
https://codeforces.com/contest/575/problem/A
https://codeforces.com/contest/575/submission/63930223
一眼题不说了,mdzz要判k=0和k=1。
47-1(19.11.1)(矩阵乘法+倍增):
https://codeforces.com/contest/576/problem/D
https://codeforces.com/contest/576/submission/63987413
设\(T(i)\)表示i时间,从1出发,能到那些点。
\(F(i)\)表示,\(i\)以前的边所形成的转移矩阵。
\(T(n+1)=T(n)*F(n)\)
而这些边按时间排序,之间的\(F\)是一样的。
用倍增去试即可,注意维护的是\(F(n)^{1..x}\)的或和。
47-2(19.11.1)(结论+二分图网络流):
https://atcoder.jp/contests/agc029/tasks/agc029_f
https://atcoder.jp/contests/agc029/submissions/8241674
什么LJ猜结论题。
我只能把题解复述一遍了。
对于一棵树,当我们去掉一个点后,剩下的点和边必须有完美匹配,可以理解为以这个点作根,每个点和它到父亲的边匹配。
对于每个点都要满足这个,这显然是有解必要条件。
其实这还是充分条件。
考虑分别以u、v作根,把两个完美匹配图取并集,你会发现一定有一条从u到v的路径,因为u、v的度数=1,而其他点的度数都=2。
这也说明假设以\(u\)为根,跑完美匹配,如有\(x,y\in s[i],s[i]~choose~x\),\(x->y 连边\)。
那么从u开始dfs,若满足之前的必要条件,一定能够走到其它的所有点(相当于沿着路径更改匹配的选择)。
这恰好也是一组答案。
10-1(19.11.2)(点分治二分树上凸函数):
https://codeforces.com/contest/566/problem/C
https://codeforces.com/contest/566/submission/64055933
考虑一条链的情况,考虑选的位置是x,则代价=\(\sum abs(p[i]-x)^{1.5}*w[i]\)。
这是若干凸函数的和,还是一个凸函数。
那么链上直接二分即可。
树上用点分治二分即可,每次看往哪个子树走优。
36-1(19.11.2)(库默尔定理+数位dp):
https://codeforces.com/contest/582/problem/D
https://codeforces.com/contest/582/submission/64057294
很久以前做过的。
https://blog.csdn.net/Cold_Chair/article/details/77488682
9-2(19.11.2)(set):
https://codeforces.com/contest/674/problem/D
https://codeforces.com/contest/674/submission/64057984
37-2(19.11.4)(动态规划):
https://codeforces.com/contest/704/problem/B
https://codeforces.com/contest/704/submission/64202127
不错的一道拆绝对值dp题。
x是递增的,那么我们不需要考虑具体是谁和谁匹配,只需要知道方向即可。
从左往右做,左边就可以剩下两类点,一类是缺一条来自右边的入边,一类是缺向左的出边,一个点可以同时是第一类和第二类。
设\(f[i][j][k]\)表示前i个点,一类点j个,二类点k个,最小值。
然而在不考虑起点和终点时,\(j=k\),因为有入必有出。
经过起点后,第二类比第一类多一个,经过终点后,第一类比第二类多一个。
这样就少了一维。
dp时可以记当前第二类比第一类多了h=0、-1、1个点。
转移时注意除了一开始和最后,第一类和第二类点至少要有一个,不然就不联通了。
4-3(19.11.4)(构造):
https://atcoder.jp/contests/agc030/tasks/agc030_c
https://atcoder.jp/contests/agc030/submissions/8291382
发现竖着横着都不行,于是就斜着。
使\(n=k,color[i][j]=(i+j)\%n+1\)。
你得到了和横着竖着一样的解法。
发现这个东西,每一条斜线可以塞两个颜色……也不会错……
2-3(19.11.4)(动态规划):
https://atcoder.jp/contests/agc030/tasks/agc030_d
https://atcoder.jp/contests/agc030/submissions/8292283
考虑设\(f[i][j]\)表示若干操作后,\(a[i]>a[j]\)的方案数。
若当前是交换\(x,y\)。
对f的影响就是,要么就是×2,即\(i、j\)与\(x、y\)无关,要么就是很简单的转移。
所以维护个整体×2标记,每次只修改相关的,最后统计一下就好了。
13-2(19.11.5)(二元关系网络流):
https://atcoder.jp/contests/agc038/tasks/agc038_f
https://atcoder.jp/contests/agc038/submissions/8299844
考虑每个环只有转一下和不转,我们可以对i讨论一下贡献。
\(1.p[i]=q[i]=i\)
不管怎样都不会有贡献。
\(2.p[i]=q[i],p[i]≠i\)
只有两个都不转或两个都转才没有贡献。
\(3.p[i]≠q[i],p[i]=i\)。
只有q转了才有贡献。
\(4.p[i]≠q[i],q[i]=i\)
只有p转了才有贡献。
\(5.p[i]≠q[i],p[i]≠i且q[i]≠i\)
只有都不转才没有贡献。
由第2条可以得到两边的方向是相反的。
不妨设\(p\)属于\(S\)即选了,\(q\)属于\(T\)即选了。
连边的话比较显然,不写了。
4-2(19.11.5)(概率+生成函数|min-max容斥+dp):
https://atcoder.jp/contests/agc038/tasks/agc038_e
https://atcoder.jp/contests/agc038/submissions/8303241
设\(P(i)\)表示i还没有结束的概率。
\(Ans=\sum_{i>=0}P(i)\)
直接算\(P(i)\)并不好算。
考虑设\(Q(i)\)表示第i步已经结束的概率。
\(P(i)=1-Q(i)\)
\(Q(i)=\sum_{d[j]>=b[j]且\sum d[j]=i}{i! \over d[j]!}*\prod({a[j]\over \sum a})^{d[j]}\)
写成EGF:
\(Q(x)=\prod (e^{{a[j]\over \sum a}x}-\sum_{k=0}^{b[j]-1}({a[j]\over \sum a})^k/k!*x^k\)
\(P(x)=e^x-Q(x)\)
暴力展开求出\(P(x)\)。
考虑最后\(P\)的形式是:
\(\sum c[i][j]*e^{{i\over \sum a}x}*x^j\)
忽略\(c[i][j]\),相当于求\(e^{tx}*x^j\)每一项系数和。
\(=\sum_{i>=0}t^i*x^j/i!*(i+j)!\)
这个是EGF,所以乘上\((i+j)!\)。
\(=j!\sum_{i>=0}t^i*C_{i+j}^j\)
\(=j!*({1\over 1 - t})^{j+1}\)
还可以min-max容斥+dp,只要有意识的靠,再推推式子就能出来,这里不讲了。
31-1(19.11.6)(扫描线):
https://codeforces.com/contest/538/problem/H
https://codeforces.com/contest/538/submission/64347902
这个题除了特别长以外就真的只是特别长了。
先对每个联通块进行单独考虑。
不是二分图直接无解。
是二分图的话,对二分图的两边分别求区间交。
现在问题就是看每一个二分图的两边分别分到那边。
假设一个二分图的两个边的区间交分别为\([l1,r1][l2,r2](l1<=l2)\)
一共有三种情况:
\(l2,r2<=r1\)
\(l2<=r1,r2>r1\)
\(l2>r1\)
对这三种情况分别看\(n1\)属于每一个子区间时,\(n2\)能属于哪个区间。
然后扫一遍,用个堆来维护即可。
最后还要还原答案,想打出题人。
29-1(19.11.6)(2-SAT):
https://codeforces.com/contest/568/problem/C
https://codeforces.com/contest/568/submission/64362913
比较显然由字典序去填每一位。
问题相当于有一些为已经确定,问是否有解。
每个点拆位选'V'还是选'C',对一开始的边,正反都连一下。
已经确定的点,可以直接dfs,或者连一条边继续判。
注意要判只有'V'或者'C'的情况。
22-2(19.11.6)(几何+简单线性规划):
https://codeforces.com/contest/685/problem/C
https://codeforces.com/contest/685/submission/64371840
考虑二维的时候,我们把曼哈顿变成\((x+y,x-y)\),这样就变成了矩形。
同样的,三维我们把它变成四维的:
\(a=x+y+z\)
\(b=x-y-z\)
\(c=-x+y-z\)
\(d=x+y-z\)
二分答案ans后,求四维空间的交。
当然并不是有交就有答案。
若有\(x,y,z\)满足\(a,b,c\),它不一定满足\(d\)。
所以要限制一下\(d\),事实上\(d=a+b+c\),专门凑好的。
那么这里做一个简单的线性规划可以解出\(a,b,c\)。
注意\((x,y,z)\)要是整数,所以反解后得是整数。
\(x=(a+b)/2\)
\(y=(a+c)/2\)
\(z=(b+c)/2\)
可以得出\((a,b,c)\)的奇偶性相同即可。
解出最优答案的\((a,b,c)\)后就好了。
不知道为什么WA on 6,可能是解\((a,b,c)\)时出了一点小问题,正负波动个2就能过了。
30-2(19.11.7)(dp):
https://codeforces.com/contest/704/problem/C
https://codeforces.com/contest/704/submission/64453278
每个点只出现两次,所以形成的图的每个联通子图要不是是环要不是链。
直接dp,要判一堆\(k=1\)带来的猎奇情况。
6-3(19.11.7)(mst):
https://atcoder.jp/contests/arc093/tasks/arc093_c
https://atcoder.jp/contests/arc093/submissions/8324595
先做出个Mst。
设mst的边权和为sum。
若\(sum>X\),显然\(ans=0\)。
若\(sum=X\),设能在mst上的边有ca个,不在有cb个。
考虑这ca个只要不全部同色就行了。
\(Ans=(2^{ca}-2)*2^{cb}\)。
若\(sum<X\),设替换mst上的最大边后\(sum>X\)的有\(cb\)个,\(sum=X\)的有ca个。
显然这ca个至少有一个和\(sum<X\)的异色。
所以\(Ans=2*(2^{ca}-1)*2^{cb}\)。
10-2(19.11.8)(SAM+dp):
https://codeforces.com/contest/700/problem/E
https://codeforces.com/contest/700/submission/64496361
考虑把这个出现两次卡的紧一些。
比如说一定有一次是后缀,这样答案并不会变。
但是因为变成了后缀,所以可以在SAM上dp。
考虑SAM上一个节点x代表着长度在一个区间的串,设\(f[x]\)表示x代表的最长串的答案。
\(f[x]\)可以由\(f[fa[x]]\)转移过来,至于为什么只考虑最长串之间的dp,可以看:
https://blog.csdn.net/litble/article/details/81179442
的证明。
判断\(f[x]\)能不能由\(f[fa[x]]\)转移过来可以预处理\(right\)集的线段树。
注意对每个x要多记\(pos[x]\),含义为:
\(pos[x]=y|y是x的祖先且f[x]=f[y]且min(dep[y])\)
每次实际转移不是\(x\)和\(fa[x]\),而是\(x\)和\(pos[fa[x]]\)。
13-1(19.11.8)(构造+欧拉回路):
https://codeforces.com/contest/528/problem/C
https://codeforces.com/contest/528/submission/64499842
胡乱构造题,自己造出来也不知道对不对。
首先度数是奇数的点肯定要补成偶数的嘛。
然后考虑对每个联通块,每个点都是偶数的度数,可以拉一条欧拉回路。
如果欧拉回路的长度是偶数,那么只要正-反-正-反…就可以满足题目条件了。
所以如果长度是奇数,随便在一个点那里加一个自环。
19-1(19.11.8)(kruskal重构树+线段树):
https://codeforces.com/contest/571/problem/D
https://codeforces.com/contest/571/submission/64509298
写完后看了别人的做法感觉自己弱爆了。
当时一看这题,把类似Kruskal重构树的东西建出来,这样一个时间内的联通的点就在一个子树里了。
然后通过线段树来打标记求出每个点最近被删的时间。
再通过线段树区间加求和来搞定一个点在一个时间以前的答案。
由于就是要做两遍几乎一样的东西,所以代码达到了200行,虽然写起来很快。
23-1(19.11.8)(2-sat构造解):
https://codeforces.com/contest/587/problem/D
https://codeforces.com/contest/587/submission/64526348
考虑每个边拆成选和不选。
同一颜色的边在同一点可以得到限制。
在同一点的边可以得到不能同时选的限制,这里新建一些前缀后缀的虚点来搞。
然后二分答案,边权>ans的不能选,跑2-sat看有没有解。
最后再随便给出一组解,这好像是我第一次写构造解。
可以看这篇博客,有许多证明:
https://blog.csdn.net/litble/article/details/80404751