• Codeforces Round #722 (Div. 2) A~F 题解


    本场链接:Codeforces Round #722 (Div. 2)

    闲话

    因为误读了D卡了一个半小时,遗憾,E会了之后再补。

    A. Eshag Loves Big Arrays

    任何一个数和一个比它小的数组合在一起一定可以把较大的数删去,数量变多之后也是一样,所以所有非最小值的数都可以删掉。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define forn(i,x,n) for(int i = x;i <= n;++i)
    #define forr(i,x,n) for(int i = n;i >= x;--i)
    #define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
    
    const int N = 105;
    int a[N];
    
    int main()
    {
        int T;scanf("%d",&T);
        while(T--)
        {
            int n;scanf("%d",&n);
            forn(i,1,n) scanf("%d",&a[i]);
            int minv = a[1];
            forn(i,1,n) minv = min(minv,a[i]);
            int res = 0;
            forn(i,1,n) if(a[i] != minv)    ++res;
            printf("%d
    ",res);
        }
        return 0;
    }
    
    

    B. Sifid and Strange Subsequences

    由于元素可以任取,所以不妨先排序。注意到最小值肯定是相邻的两个数做差取到的,如果排序后两个数违反了规则说明肯定不能放在一起,并且删掉其中一个与其他的元素组合在一起只会让情况变得更差:从左到右看能不能构成连续的段,如果可以就向右边推,否则说明一定要在这里断开重新做一段。维护最大值即可。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define forn(i,x,n) for(int i = x;i <= n;++i)
    #define forr(i,x,n) for(int i = n;i >= x;--i)
    #define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
    
    const int N = 1e5+7;
    int a[N];
    
    int main()
    {
        int T;scanf("%d",&T);
        while(T--)
        {
            int n;scanf("%d",&n);
            forn(i,1,n) scanf("%d",&a[i]);
            sort(a + 1,a + n + 1);
            int res = 1;
            forn(i,1,n)
            {
                int j = i,minv = 1e9;
                while(j + 1 <= n && min(minv,abs(a[j + 1] - a[j])) >= a[j + 1]) 
                {
                    minv = min(minv,abs(a[j + 1] - a[j]));
                    ++j;
                }
                res = max(res,j - i + 1);
                i = j;
            }
    
            printf("%d
    ",res);
        }
        return 0;
    }
    
    

    C. Parsa's Humongous Tree

    猜想:每个权值虽然给了一个范围,但是取边界值肯定不会让情况变得更差。

    考虑dp,每个元素只有选最小值和最大值区别。

    • 状态:(f[u][j = 0/1])表示对于以(u)为根的子树,(j=0)(u)取最小值,反之(u)取最大值的最大和。

    • 入口:叶子节点为(0)

    • 转移:(f[u][0] += max(f[v][0] + abs(l[v] - l[u]),f[v][1] + abs(r[v] - l[u]))).

      (f[u][1] += max(f[v][0] + abs(l[v] - r[u]),f[v][1] + abs(r[v] - r[u]))).

    • 出口:(res = max(f[u][0],f[u][1]))

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define forn(i,x,n) for(int i = x;i <= n;++i)
    #define forr(i,x,n) for(int i = n;i >= x;--i)
    #define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
    
    const int N = 1e5+7;
    vector<int> E[N];
    int l[N],r[N];
    ll f[N][2];
    
    void dfs(int u,int fa = -1)
    {
        for(auto& v : E[u])
        {
            if(v == fa) continue;
            dfs(v,u);
            f[u][0] += max(f[v][0] + abs(l[v] - l[u]),f[v][1] + abs(r[v] - l[u]));
            f[u][1] += max(f[v][0] + abs(l[v] - r[u]),f[v][1] + abs(r[v] - r[u]));
        }
    }
    
    int main()
    {
        int T;scanf("%d",&T);
        while(T--)
        {
            int n;scanf("%d",&n);
            forn(i,1,n) E[i].clear(),f[i][0] = f[i][1] = 0;
            forn(i,1,n) scanf("%d%d",&l[i],&r[i]);
            forn(i,2,n)
            {
                int u,v;scanf("%d%d",&u,&v);
                E[u].push_back(v);E[v].push_back(u);
            }
            dfs(1);
            printf("%lld
    ",max(f[1][0],f[1][1]));
        }
        return 0;
    }
    
    

    D. Kavi on Pairing Duty

    由于题目的数据范围是(10^6),不考虑公式解,考虑递推:

    • 方程:(f[i])表示(2* i)个点划分成好方案的方案数。
    • 入口:(f[0] = f[1] = 1)
    • 转移:按第一组的位置划分方案,设与(1)配对的点为(x),那么任何一个点(p in [x + 1,n])一定属于一段与([1,x])段相同长度的段中。因为他不能在一个大的内部,所以只能是长度相同的。讨论(x geq n)的情形:手推可以发现会剩下一部分完全连续的,这样方案数就对应(f[x - n - 1])(x < n)的由于长度必须是(n)的因数,所以方案数就是(D(n)),其中(D(n))(n)的约数个数(但不含有这个数本身)。递推方程:(f[i] = D(i) + sumlimits_{j = 0}^{i - 1}f[j])
    • 出口:(f[n])

    实现时维护前缀和(S)并递推即可。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pii;
    #define forn(i,x,n) for(int i = x;i <= n;++i)
    #define forr(i,x,n) for(int i = n;i >= x;--i)
    #define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
    
    const int MOD = 998244353,N = 1e6+7;
    ll f[N];
    
    int main()
    {
        forn(i,1,N - 1) for(int j = 2 * i;j <= N - 1;j += i)    ++f[j];
        ll S = 1;f[0] = 1;
        int n;scanf("%d",&n);
        forn(i,1,n)
        {
            f[i] = (f[i] + S) % MOD;
            S = (S + f[i]) % MOD;
        }
        printf("%lld
    ",f[n]);
        return 0;
    }
    
    

    E. Trees of Tranquillity

    • 对于任何一个团的点集S,他一定是S树上从根到某个叶子的路径上所有点形成的点集的子集,因为任意两个点在S树上存在祖孙关系,所以可以枚举每个S树上的叶子,维护出根到每个叶子的点集,这一步可以dfs的同时维护当前已经有的路径实现。
    • 如果(u)(v)的祖先,那么其dfs序满足:(in[u] < in[v])(out[u] > out[v])。如果写成两个区间的形式,点((u,v))之间存在祖孙关系等价于一个区间包含另一个区间。这里有一个小结论:任何两个区间只有包含和不相交的形态,不存在相交。

    首先预处理K树的dfs序,再dfs S树,过程中维护出一个点集S,我们要求:

    • 支持新加入一个点
    • 支持删去一个点
    • 点集S的大小不劣于最优解

    自上往下考虑,每次走到一个新点(u)考虑把他加入,这里有两种情况:存在一个点(p)K树中是他的儿子,产生矛盾应该删去一个点保留某个点,不难贪心想到应该保留靠下的点,因为高处的点更容易作为某个点的祖先,覆盖面更广。所以当前(u)应该不加入。另外一种情况是存在一个点(p)K树中是他的祖先,那么这个时候应该先删掉(p),再加入(u)。其他情况加入(u)即可。

    考虑使用set维护{in[v],v},根据区间不交的情况,事实上只需要找与(in[v])相临的两个点判断即可。

    整体复杂度(O(nlog(n))),可以通过本题。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pii;
    #define forn(i,x,n) for(int i = x;i <= n;++i)
    #define forr(i,x,n) for(int i = n;i >= x;--i)
    #define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
    #define x first
    #define y second
    
    const int N = 3e5+7;
    vector<int> E[N][2];
    int in[N],out[N],time_stamp,ans;
    set<pii> st;
    
    void dfs1(int u,int fa,int root)
    {
        in[u] = ++time_stamp;
        for(auto& v : E[u][root])
        {
            if(v == fa)   continue;
            dfs1(v,u,root);
        }
        out[u] = ++time_stamp;
    }
    
    void dfs2(int u,int fa,int root)
    {
        int flag = 0;
        if(st.empty())  st.insert({in[u],u});
        else
        {
            auto p = st.lower_bound({in[u],0});
            if(p != st.begin())
            {
                --p;
                if(out[(*p).y] >= in[u])
                {
                    flag = (*p).y;
                    st.erase({(*p).x,(*p).y});
                    st.insert({in[u],u});
                }
                else st.insert({in[u],u});
            }
            if(p != st.end() && (*p).x > out[u]) st.insert({in[u],u});
    
        }
        ans = max(ans,(int)st.size());
        for(auto& v : E[u][root])
        {
            if(v == fa) continue;
            dfs2(v,u,root);
        }
        if(flag)   st.insert({in[flag],flag});
        st.erase({in[u],u});
    }
    
    int main()
    {
        int T;scanf("%d",&T);
        while(T--)
        {
            int n;scanf("%d",&n);
            forn(i,1,n) E[i][0].clear(),E[i][1].clear(),time_stamp = 0,ans = 0;
            forn(i,2,n)
            {
                int p;scanf("%d",&p);
                E[i][0].push_back(p);E[p][0].push_back(i);
            }
            forn(i,2,n)
            {
                int p;scanf("%d",&p);
                E[i][1].push_back(p);E[p][1].push_back(i);
            }
            dfs1(1,-1,1);
            dfs2(1,-1,0);
            printf("%d
    ",ans);
        }
        return 0;
    }
    

    F. It's a bird! No, it's a plane! No, it's AaParsa!

    其实如果没有等待任意时间这个条件,这个题直接建图就可以了。

    如果一个点(u)在原地等待了(x)秒走到了(v),那么等价于立即从(u)跳到((v-x))(在模意义下),再走了(x)步走到(v)。因此结论:构造(n)条虚边,每条边链接(i)(i+1),边权为(1),在这样的图上求多源最短路即答案。

    由于(m=n^2)所以堆优化的dijkstra反而是负优化,注意这一点。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define forn(i,x,n) for(int i = x;i <= n;++i)
    #define forr(i,x,n) for(int i = n;i >= x;--i)
    #define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
    
    const int N = 607;
    ll g[N][N],dist[N][N];
    bool st[N];
    int n,m;
    
    void dijkstra(int STT)
    {
        forn(i,0,n - 1) dist[STT][i] = 1e18;
        forn(i,0,n - 1) st[i] = 0,dist[STT][i] = g[STT][i];
        forn(_,1,n)
        {
            int t = -1;
            forn(i,0,n - 1)     if(!st[i] && (t == -1 || dist[STT][t] > dist[STT][i]))  t = i;
            if(t == -1) break;
            st[t] = 1;
            dist[STT][(t + 1) % n] = min(dist[STT][(t + 1) % n],dist[STT][t] + 1);
            forn(j,0,n - 1)
            {
                int v = (j + dist[STT][t]) % n;
                dist[STT][v] = min(dist[STT][v],dist[STT][t] + g[t][j]);
            }
        }
        dist[STT][STT] = 0;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        forn(i,0,n - 1) forn(j,0,n - 1) g[i][j] = 1e18;
        forn(i,1,m)
        {
            int u,v,w;scanf("%d%d%d",&u,&v,&w);
            g[u][v] = min(g[u][v],1ll*w);
        }
        forn(i,0,n - 1) dijkstra(i);
        forn(i,0,n - 1)
        {
            forn(j,0,n - 1) printf("%lld ",dist[i][j]);
            puts("");
        }
        return 0;
    }
    
    
  • 相关阅读:
    Linux 搭建svn环境
    Echarts 获取后台数据 使用后台数据展示 饼装图
    js 实现存储Map 结构的数据
    SVN使用方法
    SVN版本回退
    adf 日志输出
    Oracle ADF VO排序及VO的查询模式
    weblogic 初始化
    jdeveloper 恢复默认配置
    jdeveloper12.1.3的安装与卸载
  • 原文地址:https://www.cnblogs.com/HotPants/p/14810727.html
Copyright © 2020-2023  润新知