• Codeforces Global Round 19思路分享


    Codeforces Global Round 19

    老天赏脸,又让我涨了一波分。。。

    A. Sorting Parts

    首先我们可以发现,如果序列本来就有序的话,那么无论我们选择哪个点重排最终还是有序的。考虑一个序列至少存在一个逆序对,那么我们完全可以将这个逆序对分开,这样的话,这个逆序对一定还存在,那么就是YES了。

    B. MEX and Array

    发现n才100,果断DP,直接设f[i]表示前i个数的答案即可,之后暴力枚举转移即可。

    C. Andrew and Stones

    首先考虑因为选择的中间的石头个数至少是偶数的话,那么它恰好就能分发到1和n中。倘若是奇数,那么它需要别人“施舍”个它一个石头,凑成偶数,这样的话,就可以恰好分完。考虑什么样的情况无解。2到n-1的石头中,无人可给予,且有奇数块的时候,这个时候无法完成。否则若存在石头可给予,那么整个中间的石头一定可以被送走完。注意特立n=3的情况,因为自己不能给予给自己。

    点击查看代码
    #include<bits/stdc++.h>
    #define ll long long
    #define db double
    using namespace std;
    const int N=1e5+10;
    int T,n,a[N],b[N];
    int main()
    {
    //    freopen("1.in","r",stdin);
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&n);
            for(int i=1;i<=n;++i) scanf("%d",&a[i]);
            if(n==3)
            {
                if(a[2]%2==1) puts("-1");
                else printf("%lld\n",a[2]/2);
                continue;
            }
            int cnt1=0,cnt0=0;
            ll ans=0;
            for(int i=2;i<n;++i) 
            {
                if(a[i]%2==1) cnt1++;
                else cnt0++;
                ans+=a[i]/2;
            }
            if(ans==0&&cnt1) {puts("-1");continue;}
            printf("%lld\n",ans+cnt1);
        } 
        return 0;
    } 
    

    D. Yet Another Minimization Problem

    我们把括号拆开的话,发现无论换与不换,平方项都是不变的,变得都是2ab的乘积项,通过这个我们可以发现,我们其实只需要保留前面的和即可。直接设dp,f[i][j]表示前i项,a的和为j的最小值。
    暴力转移即可。

    点击查看代码
    #include<bits/stdc++.h>
    #define ll long long
    #define db double
    using namespace std;
    const int N=110,M=10010,INF=1e9;
    int T,n,a[N],b[N],sum[N],f[N][M];
    int main()
    {
    //    freopen("1.in","r",stdin);
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&n);
            for(int i=1;i<=n;++i) scanf("%d",&a[i]);
            for(int i=1;i<=n;++i)
            {
                scanf("%d",&b[i]);
                sum[i]=sum[i-1]+a[i]+b[i];
            }
            for(int i=1;i<=n;++i)
                for(int j=0;j<=sum[n];++j) f[i][j]=INF;
            f[1][a[1]]=0;f[1][b[1]]=0;
            for(int i=2;i<=n;++i)
                for(int j=0;j<=sum[i-1];++j)
                {
                    f[i][j+a[i]]=min(f[i][j+a[i]],f[i-1][j]+2*j*a[i]+2*(sum[i-1]-j)*b[i]);
                    f[i][j+b[i]]=min(f[i][j+b[i]],f[i-1][j]+2*j*b[i]+2*(sum[i-1]-j)*a[i]);
                }
            int ans=INF;
            for(int j=0;j<=sum[n];++j) ans=min(ans,f[n][j]);
            for(int i=1;i<=n;++i)
                for(int j=i+1;j<=n;++j) ans+=a[i]*a[i]+b[i]*b[i]+a[j]*a[j]+b[j]*b[j];
            printf("%d\n",ans);    
        } 
        return 0;
    } 
    

    E. Best Pair

    结束前1分多钟交上去,没想到直接过了,始料未及....
    首先我们肯定要将输入的数据进行整理,离散化,统计个数...统计每个数出现的个数即原本的值。之后考虑最大的f(u,v),由于是(cntx+cnty)⋅(x+y).的关系,我们观察到,在cnt相等的情况下,本身的权值越大越好,所以我们可以想到将所有的数按照cnt分组,之后考虑这样分组的话,组数最多是sqrt(n)的。考虑答案一定是某一组内的两个数的结果,要么是某两个不同组内的两个数的结果过。先考虑第一个,同组内的话,我们依次枚举每一个数,考虑它的另一个数,直接按照权值从大到小枚举,找到第一个合法的之后就可以直接break了,因为后面的一定没这个优。同理,两组的也这么做。考虑这样做的复杂度,由于坏的匹配只有m个,所以我们最多把这m个匹配找完就不会做无用功了。总的复杂度为nlogn。由于我太懒,当时又太紧张,离散化,没有预处理,导致我写的代码是nlog^2,不过还是过了,谢天谢地...

    点击查看代码
    #include<bits/stdc++.h>
    #define ll long long
    #define db double
    using namespace std;
    const int N=3e5+10;
    int T,n,m,a[N],b[N],num,cnt[N];
    struct wy{int val,cnt;}q[N];
    map<int,bool>mp[N];
    inline int find(int x){return lower_bound(b+1,b+num+1,x)-b;}
    vector<pair<int,int> >v[N];
    inline bool cmp(wy a,wy b)
    {
        if(a.cnt==b.cnt) return a.val>b.val;
        return a.cnt<b.cnt;
    }
    int main()
    {
    //    freopen("1.in","r",stdin);
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&n,&m);
            for(int i=1;i<=n;++i) scanf("%d",&a[i]),b[i]=a[i];
            sort(b+1,b+n+1);
            num=unique(b+1,b+n+1)-b-1;
            for(int i=1;i<=num;++i) mp[i].clear(),cnt[i]=0,v[i].clear();
            for(int i=1;i<=n;++i) cnt[find(a[i])]++;
            for(int i=1;i<=num;++i) q[i].cnt=cnt[i],q[i].val=b[i];
            sort(q+1,q+num+1,cmp);
            for(int i=1;i<=m;++i)
            {
                int x,y;scanf("%d%d",&x,&y);
                int t1=find(x),t2=find(y);
                mp[t1][t2]=1;mp[t2][t1]=1;
            }
            int tot=0;
            for(int i=1;i<=num;++i)
            {
                int j=i;
                while(j+1<=num&&q[j+1].cnt==q[i].cnt) ++j;
                ++tot;
                for(int k=i;k<=j;++k) v[tot].push_back({q[k].val,q[k].cnt});
                i=j; 
            }
            ll ans=0;
            for(int i=1;i<=tot;++i) 
            {
                for(int j=0;j<v[i].size();++j)
                {
                    for(int k=j+1;k<v[i].size();++k)
                    {
                        pair<int,int>x=v[i][j],y=v[i][k];
                        if(mp[find(x.first)].find(find(y.first))!=mp[find(x.first)].end()) continue;
                        ans=max(ans,(ll)(x.first+y.first)*(y.second+x.second));
                        break;
                    }
                }
                for(int j=i+1;j<=tot;++j)
                {
                    for(int l=0;l<v[i].size();++l)
                    {
                        for(int k=0;k<v[j].size();++k)
                        {
                            
                            pair<int,int>x=v[i][l],y=v[j][k];
                            if(mp[find(x.first)].find(find(y.first))!=mp[find(x.first)].end()) continue;
                            ans=max(ans,(ll)(x.first+y.first)*(y.second+x.second));
                            break;
                        }
                    }
                }
            }
            printf("%lld\n",ans);
        } 
        return 0;
    } 
    

    F. Towers

    看到题,经过一番思索后发现这个题有几个关键的点:
    1.最优答案一定是只在度数为1的点上设置塔楼。
    2.整个树中权值最大的点比较关键。
    首先我们可以想象出某个点x的权值最大为\(h_{max}\),那么也就是说必定会有某两个度数为1的点,x在这两个点的路径中,这两个点的\(e_i\)都为\(h_{max}\)。我们可以干脆的以权值最大的点为根。那么对于当前树中某个点i,他能够收到信号的条件,他在某两个度数为1的路径中,且这两个点的\(h_i\)都大于等于\(h_i\)。考虑到我们在最开始时已经设置了两个点的权值为\(h_max\)。那么当前点\(i\)一定能通过某种路径到达其中某个点,接下来我们只要保证它在子树内的某个叶子节点的\(e\)>=\(h_i\)即可。最初我们可以将所有度数为1的地点都设为他们初始的\(h_i\)。之后网上做dp,保留当前节点x的所有叶子节点设定的最大的\(e_i\),然后比大小,显然我们让原本中最大的\(h_i\)修改最优。至于初始确定的最大的两个节点。我们可以最后确定。

    点击查看代码
    #include<bits/stdc++.h>
    #define ll long  long
    using namespace std;
    const int N=2e5+10;
    int n,h[N],root,f[N],mx;
    ll ans;
    vector<int>son[N];
    inline void dfs(int x,int fa)
    {
        int ch=0;
        for(auto y:son[x])
        {
            if(y==fa) continue;
            ch++;
            dfs(y,x);
            f[x]=max(f[x],f[y]);
        }
        if(x!=root&&h[x]>f[x]) 
        {
            ans+=h[x]-f[x];
            f[x]=h[x];
        }
        if(x==root)
        {
            if(ch==1) ans+=((f[x]>=h[x])?0:(h[x]-f[x]))+h[x];
            else
            {
                int mx1=0,mx2=0;
                for(auto y:son[x])
                {
                    if(f[y]>mx1) mx2=mx1,mx1=f[y];
                    else if(f[y]==mx1) mx2=mx1;
                    else mx2=max(mx2,f[y]);
                }
                ans+=((mx1>=h[x])?0:(h[x]-mx1))+((mx2>=h[x])?0:(h[x]-mx2)); 
            }
        }
    }
    int main()
    {
    //    freopen("1.in","r",stdin);
        scanf("%d",&n);
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&h[i]);
            if(h[i]>mx) mx=h[i],root=i;  
        } 
        for(int i=1;i<n;++i)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            son[x].push_back(y);
            son[y].push_back(x);
        }
        dfs(root,0);
        printf("%lld",ans);
        return 0;
    } 
    
  • 相关阅读:
    STM32F407 窗口看门狗 个人笔记
    Hadoop技巧系列索引
    从零自学Hadoop系列索引
    从零自学Hadoop(25):Impala相关操作下
    从零自学Hadoop(24):Impala相关操作上
    从零自学Hadoop(23):Impala介绍及安装
    Hadoop技巧(04):简易处理solr date 时区问题
    从零自学Hadoop(22):HBase协处理器
    从零自学Hadoop(21):HBase数据模型相关操作下
    从零自学Hadoop(20):HBase数据模型相关操作上
  • 原文地址:https://www.cnblogs.com/gcfer/p/15890459.html
Copyright © 2020-2023  润新知