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;
} 
posted @ 2022-02-13 21:31  逆天峰  阅读(119)  评论(0编辑  收藏  举报
作者:逆天峰
出处:https://www.cnblogs.com/gcfer//