• Educational Codeforces Round 81 (Rated for Div. 2)


    A - Display The Number

    题意:给n根火柴,拼出最大的数字。

    题解:肯定是数字越多越大,所以尽可能多拿最便宜的2根火柴一个“1”,多余的肯定是拿一个“7”,由于n>=2,没有特例。

    void test_case() {
        int n;
        scanf("%d", &n);
        if(n % 2 == 1) {
            printf("%d", 7);
            n -= 3;
        }
        while(n) {
            printf("%d", 1);
            n -= 2;
        }
        printf("
    ");
    }
    

    C - Obtain The String

    题意:给两个字符串s和t,要求从t里面选尽可能少的次数,每次选t的一个子序列,然后接在初始为空的字符串z的末尾,用最少的次数使得z变成s。

    题解:由于每次选的是子序列,所以每次可以贪心匹配最长的串,所以可以整出一个显而易见的dp。nxt[i][j]表示[i,tl]中的下一个字母j的下标,这个可以倒着直接转移出来,然后枚举s的每个字符,假如还能往后匹配就往后匹配,否则必须从头开始匹配。

    D - Same GCDs

    题意:给定 (a)(m) ,求 (sumlimits_{i=a}^{a+m-1}[gcd(i,m)=gcd(a,m)])

    首先设 (g=gcd(a,m)) 那么求 (sumlimits_{i=a}^{a+m-1}[gcd(i,m)=g])

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

    变成求一个经典问题, (n) 以内与 (m) 互质的数的个数。 (sumlimits_{i=1}^{n}[gcd(i,m)=1])

    模板库里面掏出这个问题的接口,调用可以得到结果。


    其实由于 (gcd(a,m)=gcd(amod m,m)) (辗转相除法?欧几里得算法?),

    (sumlimits_{i=a}^{a+m-1}[gcd(i,m)=g])(sumlimits_{i=1}^{m}[gcd(i,m)=g]) 除以g后即为为欧拉函数的定义 (varphi(frac{m}{g}))

    去模板库里面掏出这个问题的接口,调用可以得到结果。

    E - Permutation Separation

    题意:给一个[1,n]的permutation,称为A。每个数字带有一个权重p,要求把序列初始分为两个非空的前缀L和后缀R,然后搬动其中的元素使得P中的每个元素小于S中的每个元素,或其中之一为空。求最小代价。

    题解:有一个显然的n^2暴力,枚举切割前后缀的位置,然后再枚举搬动元素的分界线。昨天晚上学长提了一下线段树,想了一天终于有点明白了,用线段树可以快速转移出不同搬动元素的分界线的最小值,然后只需要枚举切割前后缀的位置就可以了。

    从最简单的情况入手:设一棵n个叶子的线段树st,其中每个节点[l,r]表示把[l,r]区间内的数分为前后两块所需要的最小代价cost,初始每个数字叶子会被分出自己的初始属性L或者R,当两个L叶子合并或者两个R叶子合并不需要任何代价。左边是L右边是R的节点本身就合法,合并也不需要任何代价,左边是R右边是L的话,要么把L移动到R,要么把R移动到L,代价就是两者cost的min。

    由此出发,考虑更一般的情况设cost(l,r)表示把[l,r]区间分为两堆不交叉的L和R的最小代价,costL(l,r)为把[l,r]中所有初始在R的集合搬到L的代价(维护就是简单加法),costR(l,r)同理。

    那么当区间[l,m],[m+1,r]合并,可以口胡出,分界线必定在[l,m]或者[m+1,r]之中。假如分解线在[l,m]中,那么cost(l,r)=cost(l,m)+costR(m+1,r)(把前缀中的大的元素全部移动到后缀),假如分界线在[m+1,r]中,同理cost(l,r)=cost(m+1,r)+costL(l,m)。

    所以每次就把原来的permutation里面的分界线元素的costL和costR属性取反,只需要在线段树里面Update一下。

    int n;
    int valL[200005], valR[200005];
    int a[200005];
    
    struct SegmentTree {
    #define ls (o<<1)
    #define rs (o<<1|1)
        static const int MAXN = 200000;
        ll cost[(MAXN << 2) + 5];
        ll costL[(MAXN << 2) + 5];
        ll costR[(MAXN << 2) + 5];
    
        void PushUp(int o) {
            cost[o] = min(cost[ls] + costR[rs], cost[rs] + costL[ls]);
            costL[o] = costL[ls] + costL[rs];
            costR[o] = costR[ls] + costR[rs];
        }
    
        void Build(int o, int l, int r) {
            if(l == r) {
                cost[o] = 0;
                costL[o] = valL[l];
                costR[o] = valR[l];
                //printf("[%d,%d],cost=%d costL=%d costR=%d
    ",l,r,cost[o],costL[o],costR[o]);
            } else {
                int m = l + r >> 1;
                Build(ls, l, m);
                Build(rs, m + 1, r);
                PushUp(o);
                //printf("[%d,%d],cost=%d costL=%d costR=%d
    ",l,r,cost[o],costL[o],costR[o]);
            }
        }
    
        void Update(int o, int l, int r, int q) {
            if(l == r) {
                cost[o] = 0;
                costL[o] = valL[l];
                costR[o] = valR[l];
                //printf("[%d,%d],cost=%d costL=%d costR=%d
    ",l,r,cost[o],costL[o],costR[o]);
            } else {
                int m = l + r >> 1;
                if(q <= m)
                    Update(ls, l, m, q);
                if(q >= m + 1)
                    Update(rs, m + 1, r, q);
                PushUp(o);
                //printf("[%d,%d],cost=%d costL=%d costR=%d
    ",l,r,cost[o],costL[o],costR[o]);
            }
        }
    
        ll Query(int o, int l, int r, int ql, int qr) {
            if(ql <= l && r <= qr) {
                return cost[o];
            } else {
                int m = l + r >> 1;
                ll res = LINF;
                if(ql <= m)
                    res = Query(ls, l, m, ql, qr);
                if(qr >= m + 1)
                    res = min(res, Query(rs, m + 1, r, ql, qr));
                return res;
            }
        }
    #undef ls
    #undef rs
    } st;
    
    void test_case() {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &valL[a[i]]);
    
        st.Build(1, 1, n);
        ll ans = LINF;
        for(int i = 1; i < n; ++i) {
            valR[a[i]] = valL[a[i]];
            valL[a[i]] = 0;
            /*for(int j=1;j<=n;++j)
                printf(" %d",valL[a[j]]);
            puts("");
            for(int j=1;j<=n;++j)
                printf(" %d",valR[a[j]]);
            puts("");*/
            st.Update(1, 1, n, a[i]);
            ans = min(ans, st.Query(1, 1, n, 1, n));
            //printf("ans=%d
    ",st.Query(1, 1, n, 1, n));
            //puts("---");
        }
        printf("%lld
    ", ans);
    }
    

    F - Good Contest

    2020东秦ccpc wannfly camp好像见过?错过了一场上橙的机会,不仅如此还在蓝名苟住了?

    题意:给一堆n个(至多50个)[li,ri],第i个数等概率出现在[li,ri]之间,求整个序列没有任何逆序的概率。

    题解:因为至多50个,所以可以随便乱搞。假如[l,r]的区间不大,可以有一种简单的dp方法:

    (dp[i][j]) 表示前 (i) 个数没有逆序,且最后一个数大小为 (j) 时,整个序列没有任何逆序的概率,注意这里最后一位是 (j) 的概率应该是 (1)


    (j) 在合法范围内:
    (dp[i][j]=frac{1}{r_{i-1}-l_{r-1}+1}sumlimits_{k=0}^{j}dp[i-1][k])

    否则:
    (dp[i][j]=0)

    可惜这里[l,r]范围好大哦。

  • 相关阅读:
    Javascript DOM 编程艺术读书笔记16/03/25
    2014 Multi-University Contest 1.1 hdu4861 打表找规律
    汇编小记16/3/23
    汇编小记16/3/22
    hdoj 4940 强连通图
    Head FIRST HTML & CSS 16/03/17
    html&css一些有用的网站整理
    dosbox+debug 模拟dos
    汇编小记16/3/15
    解决windwos另存为,保存文件时无法选择“桌面”文件夹
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12241986.html
Copyright © 2020-2023  润新知