codeforces 1856(div2)题解

Problem - A - Codeforces

暴力贪心,对于相邻的两个数字a[i]和a[i+1]如果a[i]>a[i+1]那么最少需要a[i]次操作才可以让a[i]<=a[i+1],那么对于所有的这样的a[i]选择最大的即可

 #include<bits/stdc++.h>
 using namespace std;
 typedef long long ll;
 typedef pair<int,int> pii;
 #define all(a) a.begin(),a.end()
 #define fr(a,i,n) for(int i=a;i<=n;i++)
 #define fe(a,i,n) for(int i=n;i>=a;i--)
 #define fcf(i,cur) for(int i=cfs.h[cur],sup=cfs.e[i];i;i=cfs.net[i],sup=cfs.e[i])
 #define endl "\n"
 istream &operator>>(istream &in,pii &a){return in>>a.first>>a.second;};
 ostream &operator<<(ostream&,pii &a){return cout<<a.first<<' '<<a.second;};
 template<typename T>
 istream &operator>>(istream &in,vector<T>&v){T x;in>>x;v.push_back(x);return in;};
 string YES="YES",Yes="Yes",yes="yes",NO="NO",No="No",no="no";
 const int N=2e5+10,M=1e6+10,mod=1e9+7;
 string s;
 int n,m,k;
 int a[N];
 void solve(){
    cin>>n;
    fr(1,i,n)cin>>a[i];
    int ans=0;
    fr(2,i,n){
        if(a[i-1]>a[i])ans=max(ans,a[i-1]);
    }
    cout<<ans<<endl;
 }
 int main(){
     ios::sync_with_stdio(false);
    int t=1;
     cin>>t;
     fr(1,i,t){
         solve();
     }
 }

Problem - B - Codeforces

也是一个贪心的思想,对于a[i]为1的时候a[i]最少为2,否则最小为1,如果所有的拿最小要超过数组a所有元素的和的话,那么就肯定是不可能的了,否则多余的可以全部放到a[i]上.

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define all(a) a.begin(),a.end()
#define fr(a,i,n) for(int i=a;i<=n;i++)
#define fe(a,i,n) for(int i=n;i>=a;i--)
#define fcf(i,cur) for(int i=cfs.h[cur],sup=cfs.e[i];i;i=cfs.net[i],sup=cfs.e[i])
#define endl "\n"
istream &operator>>(istream &in,pii &a){return in>>a.first>>a.second;};
ostream &operator<<(ostream&,pii &a){return cout<<a.first<<' '<<a.second;};
template<typename T>
istream &operator>>(istream &in,vector<T>&v){T x;in>>x;v.push_back(x);return in;};
string YES="YES",Yes="Yes",yes="yes",NO="NO",No="No",no="no";
const int N=2e5+10,M=1e6+10,mod=1e9+7;
string s;
int n,m,k;
int a[N];
void solve(){
    cin>>n;
    fr(1,i,n)cin>>a[i];
    ll sum=0;
    fr(1,i,n)sum+=a[i];
    ll cnt=0;
    fr(1,i,n)if(a[i]==1)cnt++;
    if(n==1||cnt*2+(n-cnt)>sum){
        cout<<NO<<endl;
        // return ;
    }else{
        cout<<YES<<endl;
    }
}
int main(){
    ios::sync_with_stdio(false);
    int t=1;
    cin>>t;
    fr(1,i,t){
        solve();
    }
}

Problem - C - Codeforces

二分答案

对于每个位置都二分在k次操作下能够选定的最大值,对于第i个位置假设一定是最大值的话,会出现a[i]=x,a[i+1]=x-1,a[i+2]=x-2,然后到某一个并不需要操作的位置j停止,

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define all(a) a.begin(),a.end()
#define fr(a,i,n) for(int i=a;i<=n;i++)
#define fe(a,i,n) for(int i=n;i>=a;i--)
#define fcf(i,cur) for(int i=cfs.h[cur],sup=cfs.e[i];i;i=cfs.net[i],sup=cfs.e[i])
#define endl "\n"
istream &operator>>(istream &in,pii &a){return in>>a.first>>a.second;};
ostream &operator<<(ostream&,pii &a){return cout<<a.first<<' '<<a.second;};
template<typename T>
istream &operator>>(istream &in,vector<T>&v){T x;in>>x;v.push_back(x);return in;};
string YES="YES",Yes="Yes",yes="yes",NO="NO",No="No",no="no";
const int N=2e5+10,M=1e6+10,mod=1e9+7;
string s;
ll n,m,k;
ll a[N];
int check(ll x,ll inx){
    ll cnt=0;
    fr(inx,i,n){
        ll cur=x-(i-inx);
        if(a[i]>=cur)return 1;
        cnt+=cur-a[i];
        if(cnt>k)return 0;
    }
    if(a[n]<x-(n-inx))return 0;
    return 1;
}
void solve(){
    cin>>n>>k;
    fr(1,i,n)cin>>a[i];
    ll ans=a[1];
    fr(1,i,n){
        ll l=ans,r=a[i]+k;
        while(l<=r){
            ll mid=(l+r)>>1;
            if(check(mid,i)){
                l=mid+1;
            }else{
                r=mid-1;
            }
        }
        ans=max(ans,l-1);
    }
    cout<<ans<<endl;
}
int main(){
    ios::sync_with_stdio(false);
    int t=1;
    cin>>t;
    fr(1,i,t){
        solve();
    }
}

Problem - D - Codeforces

分治

对于一个区间两个区间[l,mid],[mid+1,r]假设我都已经知道了最大值的位置inx1和inx2,如何去计算inx1和inx2那个更加的大呢?,如果假设inx1是最大的那么询问的区间[inx1,inx2]包含inx1和不包含inx1产生的差值一定是inx2-inx1,因为如果inx1是最大的那么对于区间[inx1+1,inx2]内所有的元素都会产生一个逆序对,数量刚好是inx2-inx1,通过分治从小区间开始计算出每个区间的最大值,每次的花费都不超过2*len^2,最终的花费不会超过3*n^2

还有就是一点点小细节

 

这两个写法好像是一样的,对于后面这个cf的判题机是直接输出"! "后继续计算,而不是递归结束后统一输出,就会变成这样,被编译器坑了

 Problem - E2 - Codeforces

多重背包+bitset优化

很容易发现其实最终是解决一个这样的问题:对于每个节点假设他有m个子节点,第i个子节点所在子树的节点数量为a[i]那么就是将这m个子节点分成两份,使得这两份子节点的数量和的乘积最大

对于每个节点i假设节点i所在子树除了i以外的节点数量为sum,要取得乘积的最大值那么分成两份的和a,b(a+b=sum),其中a或b要尽可能的靠近sum/2,这样a*b的值才能最大,很显然是一个背包问题,计算过程中会出现非常多的重复元素,所以使用多充背包再结合bitset优化.

其中bitset优化的时候开的size大小肯定是大于sum,然后越小越好,bitset不支持变量定义大小,就需要运用一定的小技巧(题解学的)

倍增为大于sum的最小的2^k

最终的复杂度为nsqrt(n)  本人比较的菜,无法给出证明

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define all(a) a.begin(),a.end()
#define fr(a,i,n) for(int i=a;i<=n;i++)
#define fe(a,i,n) for(int i=n;i>=a;i--)
#define fcf(i,cur) for(int i=cfs.h[cur],sup=cfs.e[i];i;i=cfs.net[i],sup=cfs.e[i])
#define endl "\n"
istream &operator>>(istream &in,pii &a){return in>>a.first>>a.second;};
ostream &operator<<(ostream&,pii &a){return cout<<a.first<<' '<<a.second;};
template<typename T>
istream &operator>>(istream &in,vector<T>&v){T x;in>>x;v.push_back(x);return in;};
string YES="YES",Yes="Yes",yes="yes",NO="NO",No="No",no="no";
const int N=1e6+10,M=2e6+10,mod=1e9+7;
string s;
int n,m,k;
vector<int>e[N];
vector<int>v;
int vis[N],son[N];
struct CFS{
    int h[N]={0},e[M]={0},v[M]={0},net[M]={0},top=0;
    void add(int a,int b){
        top++;
        e[top]=b;
        net[top]=h[a];
        h[a]=top;
        return ;
    }
    void add(int a,int b,int c){
        top++;
        e[top]=b;
        v[top]=c;
        net[top]=h[a];
        h[a]=top;
        return ; 
    }
    void init(int n){
        top=0;
        fr(1,i,n)h[i]=0;
    }
}cfs;
template<int siz=1>
ll run(int len){
    if(v.size()<=1)return 0;
    if(siz<=len)return run<min(siz*2,N-1)>(len);
    bitset<siz>dp;
    dp[0]=1;
    for(auto inx:v)dp|=(dp<<inx);
    fe(0,i,len)if(dp[i])return i;
}
ll ans;
void dfs(int cur){
    int mx=0;
    fcf(i,cur){
        dfs(sup);
        son[cur]+=son[sup];
        mx=max(mx,son[sup]);
    }
    if(mx>=son[cur]/2){
        ans+=1LL*mx*(son[cur]-mx);
        son[cur]++;
        return ;
    }
    v.clear();
    vector<int>tmp;
    fcf(i,cur){
        if(!(vis[son[sup]]++))tmp.push_back(son[sup]);
    }
    for(auto inx:tmp){
        int cnt=1;
        while(vis[inx]){
            v.push_back(min(cnt,vis[inx])*inx);
            vis[inx]-=min(vis[inx],cnt);
            cnt*=2;
        }
    }
    ll res=run(son[cur]/2);
    ans+=res*(son[cur]-res);
    son[cur]++;
}
void solve(){
    cin>>n;
    fr(2,i,n){
        int a;
        cin>>a;
        cfs.add(a,i);
    }
    dfs(1);
    cout<<ans<<endl;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int t=1;
    // cin>>t;
    fr(1,i,t){
        solve();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值