• Codeforces Round #635 (Div. 1)


    A. Linova and Kingdom (CF 1336 A)

    题目大意

    给定一棵(n)个节点(从(1)开始)的树,(1)号节点为根。每个节点要么是工厂要么是旅游景点(包括根)。给定一个数(k),表示将(k)个节点变为工厂,最大化每个工厂到根节点的路径上的旅游景点数量的和。

    解题思路

    很显然可以贪心,但是我们对于工厂位置的选择很显然从叶子节点开始,但稍加思索下可以发现难以抉择,在状态转移的时候比较复杂。

    我们可以反过来,考虑旅游景点的选择。很显然我们从根节点开始考虑。当我们选择一个节点设为旅游景点的时候,最优情况下,这个节点到根节点中的所有点一定不是工厂。

    基于这个事实,我们可以很容易算出,一个节点(a)设为旅游景点的话,它对答案的贡献即为(son[a]-deep[a]),其中(son[a])表示以(a)为根节点的子树的孩子数量(不包括它本身),(deep[a])表示节点(a)的深度(从(0)开始)。

    这两个数组一遍(DFS)即可求出,然后排序,前(n-k)大的数的和即为答案。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    void DFS(int u,int fa,int deep[],int son[],vector<int> edge[]){
        for(auto v:edge[u]){
            if (v==fa) continue;
            deep[v]=deep[u]+1;
            ++son[u];
            DFS(v,u,deep,son,edge);
            son[u]+=son[v];
        }
    }
    
    int main(void) {
        int n,k;
        read(n);
        read(k);
        vector<int> edge[n+1];
        for(int u,v,i=1;i<n;++i){
            read(u);
            read(v);
            edge[u].push_back(v);
            edge[v].push_back(u);
        }
        int deep[n+1]={0};
        int son[n+1]={0};
        DFS(1,1,deep,son,edge);
        vector<int> val;
        for(int i=1;i<=n;++i){
            val.push_back(son[i]-deep[i]);
        }
        sort(val.begin(),val.end(),greater<int>());
        LL ans=0;
        for(int i=0;i<n-k;++i) ans+=(LL)val[i];
        write(ans,'
    ');
        return 0;
    }
    


    B. Xenia and Colorful Gems (CF 1336 B)

    题目大意

    给定三组数,要求从每组数中选择一个数(x,y,z),使得这三个数的俩俩差的平方和最小。即求(min{(x-y)^2+(y-z)^2+(z-x)^2})

    解题思路

    三个数从小到大排序有左中右三种情况。

    那么我们枚举(a)组取出来的数是中间的,(b)组是最小的,(c)组是最大的。

    然后枚举(a)组里的数(x),找出(b)组里第一个小于等于(x)的数(y),找出(c)组里第一个大于等于(x)的数(z),计算它们俩俩差的平方和取最小值即可。

    其实找(x)(y)可以不用(lower\_bound)用俩指针扫也行。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    LL cal(LL a,LL b,LL c){
        return (a-b)*(a-b)+(a-c)*(a-c)+(b-c)*(b-c);
    }
    
    void work(vector<LL> num[],int a,int b,int c,LL &ans){
        for(auto i:num[a]){
            auto l=upper_bound(num[b].begin(),num[b].end(),i);
            if (l==num[b].begin()) continue;
            --l;
            auto r=lower_bound(num[c].begin(),num[c].end(),i);
            if (r==num[c].end()) continue;
            ans=ans==-1?cal(i,*l,*r):min(ans,cal(i,*l,*r));
        }
    }
    
    int main(void) {
        int t;
        read(t);
        while(t--){
            LL ans=-1;
            int cnt[3]={0};
            for(int i=0;i<3;++i) read(cnt[i]);
            vector<LL> num[3];
            for(int i=0;i<3;++i){
                for(int u,j=0;j<cnt[i];++j){
                    read(u);
                    num[i].push_back((LL)u);
                }
                sort(num[i].begin(),num[i].end());
            }
            work(num,0,1,2,ans);
            work(num,0,2,1,ans);
            work(num,1,0,2,ans);
            work(num,1,2,0,ans);
            work(num,2,0,1,ans);
            work(num,2,1,0,ans);
            write(ans,'
    ');
        }
        return 0;
    }
    


    C. Kaavi and Magic Spell (CF 1336 C)

    题目大意

    给定两个字符串(S、T),要求通过以下两个操作来构造一个字符串(A)

    • (S)的首字符放到(A)的首部
    • (S)的首字符放到(A)的尾部

    可以进行的操作数小于等于(n)(n)为串(S)的长度。

    如果串(T)是串(A)的前缀,我们说串(A)是具有膜法的。问你有多少种方法去构造一个具有膜法的串(A)。答案模(998244353)

    解题思路

    如果我们从首字母开始考虑构造,当字母放到首部的时候,会对我们判断串(T)是否为串(A)的前缀有很大影响,还会发现转移具有后效性,我们得换个方向取考虑。

    从最后一个字母来考虑的话,由于构造的串(A)的长度是已知的,这个字母只能放在当前考虑区间的首部或尾部,然后我们就可以缩小考虑范围继续考虑下一个字母,是同样的子问题。

    (dp[l][r])表示构造串(A)([l...r))(从(0)开始)的所有方法中,满足串(A)是膜法的方法的数量。

    转移就很简单了。

    对于当前考虑的字母(S[pos]),如果(l>m-1)或者(S[pos]==T[l])(即当前位不会影响判断串(T)是否为(A)的前缀或者填了是保持串(T)是串(A)的前缀的必要条件),则字母(S[pos])可以放在第(l)位,此时(dp[l][r]+=dp[l+1][r])

    如果(r>m)或者(S[pos]==T[r-1])(即当前位不会影响判断串(T)是否为(A)的前缀或者填了是保持串(T)是串(A)的前缀的必要条件),则字母(S[pos])可以放在第(r-1)位,此时(dp[l][r]+=dp[l][r-1])

    我们枚举操作数量(i=m)(n)(m)为串(T)的长度),求得对应的(dp[0][i])求和即是答案。

    然而时间复杂度为(O(n^{3}))会炸。

    因为求一次(DP)的时间复杂度为(O(n^2))是已经是它的下限了,我们只能从枚举操作数量去优化。

    我们发现,如果当操作数量为(i)时,求到了一些能使串(A)具有膜法的方法数量,当操作数变为(i+1)时,我们把额外的那个字母放到串(A)的最后面,以前求到的方法仍然能够使得串(A)具有膜法。当然放到前面我们就得重新算了。

    那么我们可以直接算(dp[0][n]),在算的时候,我们记录一个数(cnt),表示我们把前(cnt)个字母都放到了后面,第(cnt+1)个字母放到了前面,这样,由于我们是从最后考虑的字母,这前(cnt)个字母显得“可有可无”,它们存不存在不会对我们的判断造成影响,而是会使得答案翻倍(当然(cnt<n-m))。

    当我们知道有(cnt)个字母放到了后面,而目前考虑的区间的答案已经知道为(dp[l][r]),那么这(cnt)个字母的有无(也就是说操作数的减少)就会产生额外的答案为(dp[l][r]*min(cnt,n-m))。(有没有它们,能够产生膜法串(A)的方法数都是(dp[l][r]))。

    时间复杂度就变为(O(n^{2}))

    我傻了,求出(dp[0][n])(sumlimits_{i=m}^{n}dp[0][i])就是答案。

    代码里的ans就是划线处的体现。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    const LL mo=998244353;
    
    char s[3006],t[3006];
    
    int ls,lt;
    
    LL dp[3006][3006];
    
    LL ans;
    
    LL DP(int pos,int l,int r,int id,int cntt){
        if (l==r) {
            ans=(ans+min(id,ls-lt))%mo;;
            return 1;
        }
        if (dp[l][r]!=-1) {
            ans=(ans+dp[l][r]*min(id,ls-lt))%mo;
            return dp[l][r];
        }
        LL qwq=0;
        if (l>lt-1||s[pos]==t[l]) qwq=(qwq+DP(pos-1,l+1,r,id==12345?cntt:id,cntt+1))%mo;
        if (r>lt||s[pos]==t[r-1]) qwq=(qwq+DP(pos-1,l,r-1,id,cntt+1))%mo;
        return dp[l][r]=qwq;
    }
    
    int main(void) {
        scanf("%s%s",s,t);
        memset(dp,-1,sizeof(dp));
        ls=strlen(s);
        lt=strlen(t);
        ans=(ans+DP(ls-1,0,ls,12345,0))%mo;
        LL qwq=0;
        for(int i=lt;i<=ls;++i)
            qwq=(qwq+dp[0][i])%mo;
        printf("%lld
    ",qwq);
        return 0;
    }
    


    (tourist翻了()

  • 相关阅读:
    1093. Count PAT's (25)
    1092. To Buy or Not to Buy (20)
    机器学习实战——k-邻近算法:约会网站
    1057. Stack (30)
    1017. Queueing at Bank (25)
    strcpy、strncpy和memcpy的用法比较
    华为笔试题--蛇形矩阵
    对于内核执行过程的理解
    pom.xml格式问题
    Json反序列化遇到的问题
  • 原文地址:https://www.cnblogs.com/Lanly/p/12714023.html
Copyright © 2020-2023  润新知