Educational Codeforces Round 81 (Rated for Div. 2)

Educational Codeforces Round 81 (Rated for Div. 2)


A
最开始理解错题目了,以前组成的数字只有0-9…
之后想爆搜0-1e6发现又算错最大值了…

最后忽然发现位数越多数字肯定越大,并且进一位只需要加一个 1 1 1,最小消耗 2 2 2;如果给出的x是奇数呢?此时最大的位数已经可以确定了,结合 7 7 7的消耗是 3 3 3,我们得出策略:将7放在第一个,剩余的用1填充即可。

B
首先从INF入手,研究这个循环节在循环一圈后起始点的大小变化。如果循环后最后的元素(即新循环的初始元素)为0,那么对于该循环中出现的数字而言他们都将循环无数次,而那些一次都没在该循环中出现过的数字则在以后也一次都不会出现;如果新循环的元素不为0,那么这个无穷循环一定会以某个单调趋势变化,从而对于给定的x而言不可能出现无数次,所以接下来要做的就是统计x出现的次数。因为经过一次循环该循环节的元素就会统一加上 d = b [ n ] , b [ i ] d=b[n], b[i] d=b[n],b[i]表示题目说明的第i位的balance值,所以如果第一次循环中的某个元素加上若干个 d d d可以得到x,那么就应该记一次数。

在计算机中可以用取模来刻画若干个,但是对于那些负数的情况取模并不能很好的辨别,为此我们应该对d的符号进行讨论后再计数。

由于 b [ − 1 ] = 0 b[-1] = 0 b[1]=0 和题目说明一个空集也是一个可行的前缀,所以如果给定 x = = 0 x == 0 x==0那么最终结果还要加上 1 1 1进行输出。

C
贪心算法。
开设26个 v e c t o r vector vector用于记录每个小写字母在 s s s出现的位置,设置 n o w now now位置记录当前在 s s s中取元素的最远位置,对 t t t中每一位元素进行搜索即可。

WA点在于
尽管刚开始就注意到了多组数据并提前就在while循环的最后先加上了clear函数,但是中途想起来要判 − 1 -1 1所以直接在中间puts("-1"); continue;…导致最后的归零函数没有被调用…

updated:HACKED

D
容易想到如果 d = ( a , m ) d=(a,m) d=(a,m)那么就等价于求满足下述等式的 x x x的个数: ( a + x d , m d ) = 1 (\frac{a+x}d, \frac m d)=1 (da+x,dm)=1
最开始以为要分类讨论,甚至想好了利用欧拉函数的证明思想来求截断(大于某个数)的欧拉函数的个数,后来发现 a + x a+x a+x大于m的时候利用最大公约数性质可以将多余的部分减去,这样两部分一组合就成为了一个完整的欧拉函数。
线性筛 & 求欧拉函数 即可。


补题

E
昨晚想的时候打算用BIT求…今天仔细考虑了下应该是个假算法。

正确的解法是:枚举分割线的位置,用最值线段树维护右边元素集合在不同情况下的最小的花费。

容易知道,如果最终的情形时两边出现空集,那么达到此状态的最小花费一定是 m i n ( a [ 1 ] , a [ n ] ) min(a[1], a[n]) min(a[1],a[n])
处理完这种情况后我们只需要考虑两边均不为空的情况:
S ( i ) S(i) S(i)表示右边集合的最终情况为 { i , i + 1 , … , n } \{i, i +1,\dots, n\} {i,i+1,,n}所需要的最小花费,用ST维护 S ( 1 ) , S ( 2 ) , … , S ( n ) S(1),S(2),\dots, S(n) S(1),S(2),,S(n)的最小值。

我们从后往前枚举分割线出现的所有位置。最初分割线处在 n − 1 n-1 n1的位置,我们初始化ST的元素。遍历 p [ i ] , i = 1 , 2 … , n − 1 p[i], i = 1, 2\dots,n-1 p[i],i=1,2,n1,当 j < = p [ i ] j<=p[i] j<=p[i] S ( j ) S(j) S(j)会右移花费 a [ i ] a[i] a[i],所以在 [ 1 , p [ i ] ] [1,p[i]] [1,p[i]]区间上加上a[i];如果 p [ n ] ! = n p[n] !=n p[n]!=n,那么对于满足 j > p [ n ] j >p[n] j>p[n] S ( j ) S(j) S(j)都要左移加上a[n],初始化一次答案。
初始化结束后,我们移动分割线,并更新ST。当分割线移动到位置 i i i时,表示将元素 p [ i ] p[i] p[i]从左集合移动到右集合,这导致了 S ( j ) S(j) S(j)不再需要花费 a [ i ] a[i] a[i]来右移元素 p [ i ] p[i] p[i],其中 j ∈ [ 1 , p [ i ] ] j ∈[1,p[i]] j[1,p[i]];同时,这也导致了 S ( j ) S(j) S(j)需要新花费 a [ i ] a[i] a[i]来左移元素 p [ i ] p[i] p[i],其 中 j ∈ [ p [ i ] + 1 , n ] 中j∈[p[i] + 1, n] j[p[i]+1,n].
使用这种策略遍历分割线并不断更新答案,分割线从 n − 1 n - 1 n1遍历到 2 2 2,这是因为第一个元素不允许被右移,不然就会出现空集,而对于空集我们是单独进行考虑的。

代码

const int maxn = 2e5 + 10;

int n;
int p[maxn];
ll a[maxn];

ll st[maxn << 2], tag[maxn << 2];

void up(int p){
    st[p] = min(st[p << 1], st[p << 1 | 1]);
}

void down(int p){
    tag[p << 1] += tag[p];
    tag[p << 1 | 1] += tag[p];
    st[p << 1] += tag[p];
    st[p << 1 | 1] += tag[p];
    tag[p] = 0;
}

void update(int p, int l, int r, int ll, int rr, long long k){
    if(ll <= l && r <= rr){
        tag[p] += k;
        st[p] += k;
        return ;
    }
    down(p);
    int mid = (l + r) >> 1;
    if(ll <= mid) update(p << 1, l, mid, ll, rr, k);
    if(rr > mid) update(p << 1 | 1, mid + 1, r, ll, rr, k);
    up(p);
}

ll sove(){
    for(int i = 1; i <= n - 1; i++) update(1, 1, n, 1, p[i], a[i]);
    if(p[n] != n) update(1, 1, n, p[n] + 1, n, a[n]);
    
    ll ans = st[1];
    for(int i = n - 1; i >= 2; i--){
        update(1, 1, n, 1, p[i], -a[i]);
        if(p[i] + 1 <= n) update(1, 1, n, p[i] + 1, n, a[i]);
        ans = min(ans, st[1]);
    }
    return ans;
}

int main(){
//    Fast;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", p + i);
    for(int i = 1; i <= n; i++) scanf("%lld", a + i);
    ll ans = min(a[1], a[n]);
    ans = min(ans, sove());
    printf("%lld\n", ans);
    return 0;
}

C
如果数据出的针对一些,原算法就会退化成 O ( n 2 ) O(n^2) O(n2)的算法。

正确的做法是记录 n x t [ i ] [ j ] nxt[i][j] nxt[i][j]表示大于等于位置 i i i的第一个 ′ a ′ + j 'a'+j a+j 字符所在的位置,这样就可以避免最坏情况下的算法退化。

修正算法

const int maxn = 1e5 + 10;

int nxt[maxn][26];
char s[maxn], t[maxn];

int main(){
//    Fast;
    int _; scanf("%d", &_);
	while(_--){
        memset(nxt, -1, sizeof nxt);
		scanf("%s %s", s, t); int d1 = (int)strlen(s), d2 = (int)strlen(t);
		for(int i = d1 - 1; i >= 0; i--){
			char c = s[i];
			for(int j = 0; j < 26; j++){
				if(c == j + 'a') nxt[i][j] = i;
				else nxt[i][j] = nxt[i + 1][j];
			}
		}
        int flag = 1;
        for(int i = 0; i < d2; i++) if(nxt[0][t[i] - 'a'] == -1) flag = 0;
        if(!flag) puts("-1");
        else{
            
            int now = -1, ans = 1;
            for(int i = 0; i < d2;){
                int c = t[i] - 'a';
                if(nxt[now + 1][c] != -1){
                    now = nxt[now + 1][c]; i++;
                }
                else{
                    now = -1; ans++;
                }
            }
            printf("%d\n", ans);
        }
	}
}




参考博客:
Educational Codeforces Round 81 (题解)

你可能感兴趣的:(ACM)