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-1over 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))*{1over 3})。
即一条线段两个点同色的概率是({1 over 3}),要使(E(i)-E(i-1))被统计到,则(i)以前的线段都要同色,所以是(3^{i-1}),长度上限是({1over3}),还要乘({1over 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)={1over 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} ceil),事实也不会要更多次。
假设要多了一次,不如不做前面的那次不满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}),其它的都小于({1over k +1}),要求利用(O(k))的空间找出这k个单词。
考虑k=1的时候,只需要记录一个单词和一个计数器,如果新的单词和记录的一样,计数器+1,否则-1,当计数器=0时,记录的单词变为新的单词。
(k>1),类似的,就记k个不同的单词和计数器,加入一个新的,有相同的就把计数器+1,否则计数器全部-1,有计数器=0的就替换。
证明就是把上面的看做每次找k+1个不同的单词删掉,最后剩下的一定是那k个单词。
对于这道题,可以看做做(k=lfloor100/p floor),用线段树维护,(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,yin 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^{{iover 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!*({1over 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