Codeforces Round #636 (Div. 3) 全题解

题目链接:
A.Candies
B.Balanced Array
C.Alternating Subsequence
D.Constant Palindrome Sum
E.Weights Distributing
F.Restore the Permutation by Sorted Segments

  • A.Candies
    思路:简单枚举即可
    AC代码:
void solve() {
    cin>>n;
    for(ll i=3,k=4;;i+=k,k*=2) {
        if(n%i==0) {
            cout<<n/i<<'\n';
            return ;
        }
    }
}
  • B.Balanced Array
    思路:简单模拟
    AC代码:
void solve(ll sum=0) {
    cin>>n;
    if(((n+1)/2)&1) {
        cout<<"NO\n";
        return ;
    }
    cout<<"YES\n";
    rep(i,n/2) {
        cout<<i*2<<' ';
        sum+=i*2;
    }
    rep(i,(n+1)/2-1) {
        cout<<i*2-1<<' ';
        sum-=i*2-1;
    }
    cout<<sum<<'\n';
}
  • C.Alternating Subsequence
    思路:贪心,连续相同符号的值取最大。
    AC代码:
void solve(ll sum=0) {
    cin>>n;
    ll x,y=0;
    rep(i,n) {
        cin>>x;
        if(x*y>0) y=max(x,y);
        else sum+=y,y=x;
    }
    cout<<sum+y<<'\n';
}
  • D.Constant Palindrome Sum
    思路:对于每一对值,存在一个范围,最终的x如果在这个范围里,那么对于这对值只需要修改一个数或不需要修改,反之两个数都需要修改。
    这个范围D为:[min(x,y)+1,max(x,y)+k]
    我们需要得到对于每一个可能的x,其在多少对元素的范围D内,所以可以想到区间修改,单点查询的解法。
    因为是离线的,所以用差分数组+前缀和即可实现,我这里用了树状数组实现。
    AC代码:
void solve(ll ans=0x3f3f3f3f) {
    cin>>n>>k;
    rep(i,2*k) sum[i]=c[i]=0;
    rep(i,n/2) cin>>a[n/2-i+1];
    rep(i,n/2) {
        cin>>x;
        int l=min(x,a[i])+1;
        int r=max(x,a[i])+k;
        sum[x+a[i]]++;
        for(int j=l;j<=2*k;j+=j&-j) c[j]++;
        for(int j=r+1;j<=2*k;j+=j&-j) c[j]--;
    }
    rep(i,2*k) {
        ll t=0;
        for(int j=i;j;j-=j&-j) t+=c[j];
        ans=min(ans,n-t-sum[i]);
    }
    cout<<ans<<'\n';
}
  • E.Weights Distributing
    思路:预处理出每个点到a,b,c三点的距离(边数),可以想到一个好的方案一定是a到b和b到c间包含尽可能多的重复,在此基础上其余边尽可能少。
    可以贪心的认为所有的重复路径一定与b点直接相连(i.e.最终方案一定是这样的:a走过几条边到某点d,经过重复利用的边集v进入b,再走过v到d,最后走到c)于是我们只需要枚举每个d点计算出max{dst(a to d)+dst(d to b)*2+dst(d to c)}即可。
    AC代码:
const int N=4e5+5;
ll n,m,k,x,y,U,V,W;
ll sum[N],a[N],dst[4][N];
vector<int> ve[N];
 
void bfs(int V,int t) {
    queue<int> que;
    que.push(V),dst[t][V]=0;
    while(!que.empty()) {
        int u=que.front();
        que.pop();
        for(int v:ve[u]) if(dst[t][v]==-1) {
             dst[t][v]=dst[t][u]+1;
             que.push(v);
        }
    }
}
 
void solve(ll ans=1e18) {
    cin>>n>>m>>U>>V>>W;
    rep(j,3) rep(i,n) dst[j][i]=-1;
    rep(i,n) ve[i].clear();
    rep(i,m) cin>>a[i];
    sort(a+1,a+m+1);
    rep(i,m) sum[i]=sum[i-1]+a[i];
    rep(i,m) {
        cin>>x>>y;
        ve[x].push_back(y);
        ve[y].push_back(x);
    }
    bfs(V,1),bfs(U,2),bfs(W,3);
    rep(i,n) {
        ll d1=dst[1][i],d2=dst[2][i],d3=dst[3][i];
        if(d1+d2+d3>m) continue;
        ans=min(ans,sum[d1+d2+d3]+sum[d1]);
    }
    cout<<ans<<'\n';
}
  • F.Restore the Permutation by Sorted Segments
    思路:枚举待求排列的第一位数,即可推出后续所有元素。
    具体实现:将每对l~r间所有元素存到集合中。每次删除每个集合中当前推出的元素,删除后哪个集合只剩下一个元素,这个元素就一定是原数组的下一个元素,如果出现多个或没有这样的元素,则说明方案不成立。
    从思路上说其实比E更简单一些,只是代码写的稍有些乱。
    AC代码:
bool check(int u) {
    ans[1]=u;
    rep(i,n) vis[i]=0;
    for(int v:ve[u]) vis[v]=1;
    rep(i,n-1) {
        int cnt=0,t;
        for(int v:ve[u]) {
            b[v].erase(u);
            if(b[v].size()==1) cnt++,t=v;
        }
        if(cnt^1) return false;
        if(vis[t]&&num[t]!=i+1) return false;
        u=ans[i+1]=*b[t].begin();
    }
    rep(i,n) {
        if(i-1) cout<<' ';
        cout<<ans[i];
    }
    cout<<endl;
    return true;
}

void solve() {
    cin>>n;
    rep(i,n) ve[i].clear(),a[i].clear(),b[i].clear();
    rep(i,n-1) {
        cin>>num[i];
        rep(j,num[i]) {
            cin>>x;
            ve[x].push_back(i);
            a[i].insert(x);
        }
    }
    rep(i,n) {
        rep(j,n) b[j]=a[j];
        if(check(i)) return ;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值