Educational Codeforces Round 81 (Div. 2) 题解 1295A 1295B 1295C 1295D 1295E 1295F

A

输出111111111...或711111111...即可

B

多一个0,就+1;多一个1,就-1。

记录每一位的前缀和,前面每重复一次字符串s,就相当于整体加几或者减几。因此就是模运算判断一下就好。不能理解的话可以玩一玩这个:101101,然后对x从-5到5都手算一遍看看规律。

C

很经典的贪心题。一个指针(p)在s上一直循环前往后扫,另一个指针在z上,一开始指向(z_1)。如果(p)指向的字母等于(z_1),那么就把z的指针往后移一格,相当于填入了一个字母。然后(p)继续扫,直到z的指针走到结尾。

当然,这么写肯定会TLE,因此开26张表,每个表存放对应字母的出现位置,维护s的当前指针位置(p),接下来需要哪个字母了就去找这个字母的表里大于(p)的最小位置,如果不存在就把(p)放回起点,ans++。查找大于(p)的最小位置可以直接二分,直接调STL里的upper_bound就行。

无解的情况不要忘了。

D

数学题,都是老套路了。

给定(a,m),求有多少个(x)满足(0leq x < m)(gcd(a, m)=gcd(a+x,m))

翻译一下,就是求

(sumlimits_{x=0}^{m-1}[gcd(a,m)=gcd(a+x,m)])

(g=gcd(a,m)),

原式(=sumlimits_{x=0}^{m-1}[gcd(a+x,m)=g])

(=sumlimits_{x=a}^{a+m-1}[gcd(x,m)=g])

(=sumlimits_{x=1}^{a+m-1}[gcd(x,m)=g]-sumlimits_{x=1}^{a-1}[gcd(x,m)=g]).

观察(sumlimits_{i=1}^x[gcd(i,m)=g])

(=sumlimits_{i=1}^x[gcd(frac{i}{g},frac{m}{g})=1][g|i]) ((g|m))显然成立

(=sumlimits_{i=1}^{lfloorfrac{x}{g} floor}[gcd(i,frac{m}{g})=1])

(f(x,m)=sumlimits_{i=1}^x[gcd(i,m)=1]).

容斥的想法很好理解:质因数分解m,二进制状压枚举它的每个质因子选或不选,加加减减就行。下面用反演也能推出一样的结果:

(F(x,m,g)=sumlimits_{i=1}^x[g|gcd(i,m)])

(=[g|m]sumlimits_{i=1}^x[g|i])

(f(x,m,g)=sumlimits_{i=1}^x[g=gcd(i,m)])

因此(f(x,m,g)=sumlimits_{g|d}^{dleq lfloorfrac{x}{g} floor}mu(frac{d}{g})F(x,m,d))

因此(f(x,m)=f(x,m,1)=sumlimits_{d=1}^{lfloorfrac{x}{g} floor}mu(d)[d|m]sumlimits_{i=1}^x[d|i])

(=sumlimits_{d|m}^{dleq lfloorfrac{x}{g} floor}mu(d)lfloorfrac{x}{d} floor)

这个式子的含义就和上面说的容斥一样。

(mleq 10^9),因此质因子最多七八个,肯定能过。

(Ans=f(lfloorfrac{a+m-1}{g} floor, frac{m}{g})-f(lfloorfrac{a-1}{g} floor, frac{m}{g})).

E

非常巧妙的一道线段树题目。

建立一个数组(B)和一个变量(val)(b_i)表示一开始在第(i)个数后面切这一刀,之后调整两边的元素使左边小于等于(val),右边大于(val)的最小代价。

从小往大枚举(val),数组也会随之改变。如果(val)(v)变成(v+1),那么对(b_i)进行观察:

(v+1)在第(k)位(即(p_k=v+1)),

(k>i),那么原来(p_k)呆在右边,现在它要调换到左边,因此(b_i)要加上(a_k).

(k leq i),那么原来(p_k)呆在左边,需要被调换到右边,但现在不需要调换了,因此(b_i)要减去(a_k).

换句话说,就是这么两个操作:

1.区间加减一个数;

2.查询整个数组的最小值。

直接上线段树就行。

F

非常牛逼的一道dp。

一个数组,每一位都有一个随机的取值范围,问最后随机出一个不上升的数组的概率。(nleq 50, a_ileq 1e9).

实际上就是统计不下降的数组的个数(把数组倒过来看就行。。。)

这么考虑:最多2n个端点,把它们全都标到一根数轴上,把线段从左往右分成(O(n))段。一个小trick是把原来的每个右端点+1,也就是把区间从([l,r])变成([l,r+1))(S_1=<l_1,r_1>,S_2=<l_2,r_2>,...(l_1leq r_1leq l_2leq r_2...))

(f(i,j))表示数组选择了数组的前(i)个元素,且都选择在前(j)条线段上的方案数。

假如知道了(f(i,j)),那么我们可以枚举一个(k),表示在第(j+1)条线段上选择数组的接下来(k)个元素((k)可以为0)。由于线段互不相交且从左往右排序,无论在这条线段上怎么选择,最后都是一个不上升的数组。

那么就可以从状态(f(i,j))转移到(f(i+k,j+1))

最后一个问题是如何在线段(S_{j+1}=<l_{j+1},r_{j+1}>=[l_{j+1},r_{j+1}))上选择(k)个元素(可重复)?这个是非常经典的组合数学问题,求不定方程(x_1+x_2+...+x_n=k)的非负整数解的个数。运用挡板法(另一种叫法叫Stars and Bars,都一样),很容易知道这个结果是组合数(C_{k+n-1}^{n-1}=C_{k+n-1}^k).

因此有转移方程(f(i+k,j+1)+=f(i,j)*C_{k+n-1}^{k}).计算一次转移是(O(k)=O(n))的,

因为(C_{k+n-1}^k=frac{(k+n-1)!}{k!(n-1)!}=frac{1}{k!}prodlimits_{i=n}^{n+k-1}i).一个状态的前驱状态最多有(O(k)=O(n))个,而一共有(O(n*2n)=O(n^2))个状态,所以总复杂度是(O(n^4))的。事实上在转移时顺带着计算组合数就能变成(O(n^3))了(吧?)

后记

太棒了,学到许多

原文地址:https://www.cnblogs.com/zhugezy/p/12262379.html