Codeforces Round 521 (Div. 3)

目录

A. Frog Jumping

B. Disturbed People

C. Good Array

D. Cutting Out

E. Thematic Contests

F1. Pictures with Kittens (easy version)

F2. Pictures with Kittens (hard version)


A. Frog Jumping

直接模拟即可注意数据范围需要开long long

void solve(){
    
    LL a,b,k;
    cin>>a>>b>>k;
    cout << (k+1)/2*a - k/2*b << endl;
    return ;
}

B. Disturbed People

简单贪心我们可以发现如果对于一个 1 0 1 那么我调整最后一个1 变为0 是最好的因为这样让后面也只会减少贡献所以调整后面的1即可

void solve(){
    
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    
    int ans = 0;
    for(int i=1;i<=n-2;i++){
    	if(a[i] and !a[i+1] and a[i+2]){
    		ans ++ ;
    		a[i+2]=0;
    	}
    }
    cout << ans << endl;
    return ;
}

C. Good Array

直接按照题目意思模拟即可,同时注意到如果删除的数恰好是后面总和的一半的时候需要特判

void solve(){
    
    map<LL,int> mp;
    cin>>n;
    LL sum =0;
    for(int i=1;i<=n;i++){
    	cin>>a[i];
    	mp[a[i]]++;
    	sum += a[i];
    }
    vector<int> ans;
    for(int i=1;i<=n;i++){
    	LL now = sum -a[i];
    	if(now%2==0 and mp.count(now/2)){
    		if(a[i]*2==now){
    			if(mp[a[i]]>=2) ans.push_back(i);
    		}
    		else ans.push_back(i);
    	}
    }
    cout << ans.size() << endl;
    for(auto&v:ans) cout << v << ' ';
    cout << endl; 
    return ;
}

D. Cutting Out

首先我们可以简单的发现是具有二分性质的,然后我们就可以二分数量,check函数对一个数的个数整除即可,用map记录

bool check(int x){
	int cnt = 0;
	for(auto&[v,w]:mp) cnt += w/x;
	return cnt>=m;
}
void solve(){
    
    cin>>n>>m;
    for(int i=1;i<=n;i++){
    	int x; cin>>x;
    	mp[x]++;
    }
    int l=1,r=n;
    while(l<r){
    	int mid=l+r+1>>1;
    	if(check(mid)) l=mid;
    	else r=mid-1;
    }
    vector<int> res;
    for(auto&[v,w]:mp){
    	int t = w/l;
    	while(t--) res.push_back(v);
    }
    for(int i=0;i<m;i++) cout<<res[i]<<' ';
    cout<<endl;
    return ;
}

E. Thematic Contests

注意厘清题目意思,对于每一个数我们只在乎的是它的数量,然后对于题目意思的翻倍处理可以明显的知道最多只会处理20次以内2^{20}>1e6,所以对于整个数的来取得的话我们直接看后20位即可,同时对于开头第一个数肯定是 1-smax,记录一下即可,简单判断时间复杂度是可行的

map<LL,int> mp;
int check(int x){
	int sum = 0;
	for(int i=max(1,n-20);i<=n;i++){
		if(a[i]>=x){
			sum += x;
			x *= 2;
		}
	}
	return sum;
}
void solve(){
    
    cin>>n;
    for(int i=1;i<=n;i++){
    	int x; cin>>x;
    	mp[x]++;
    	smax=max(smax,mp[x]);
    }
    for(auto&[v,w]:mp){
    	a[++m]=w;
    }
    sort(a+1,a+1+m);
    
    int ans = 1;
    for(int i=1;i<=smax;i++){
    	ans = max(ans,check(i));
    }
    cout << ans << endl;
    return ;
}

F1. Pictures with Kittens (easy version)

我们可以明显的看出这是最优性问题,我们考虑dp,首先定义状态,首先第一是数量,第二维就是当前整个数选的是什么位置,同时转移来源是对于当前要选的数的前k个以内的,所以转移是ok的,同时注意求最大值,也就是一开始有些是不可以转移的,所以定义为负无穷即可

LL dp[M][M];
int w[M];
 
void solve(){
    
    cin>>n>>k>>m;
    for(int i=1;i<=n;i++) cin>>w[i];
    
    for(int i=0;i<=n;i++)
    	for(int j=0;j<=n;j++)
    		dp[i][j]=-1e18;
    
    dp[0][0]=0;
    
    for(int i=1;i<=n;i++)
    	for(int j=1;j<=i and j<=m;j++)
    		for(int u=max(i-k,0);u<i;u++){
    			dp[j][i]=max(dp[j][i],dp[j-1][u]+w[i]);
    		}
    LL ans = -1;
    for(int i=n-k+1;i<=n;i++) ans = max(ans,dp[m][i]);
    cout << ans << endl;
    return ;
}

F2. Pictures with Kittens (hard version)

依照上一题的分析同时我们注意到数据范围也就是只能是用n^2的时间复杂度来通过本题,我们在上题的分析基础上分析,可以发现dp[i](选的第i个数的转移来源 一定是选的第i-1个数)所以考虑对此优化,当我选第i个数的时候选到第j个位置时,后面的选到第j+1的数只能由dp[i-1][(max(0,j-k),j]组成这个状态的变化可以通过在转移j的同时用单调队列优化上一层的状态,这样就是满足要求的时间复杂度

LL dp[M][M];
int w[M];
 
void solve(){
    
    cin>>n>>k>>m;
    for(int i=1;i<=n;i++) cin>>w[i];
    
    if(n/k>m){
    	cout<<-1<<endl;
    	return ;
    }
    for(int i=0;i<=n;i++)
    	for(int j=0;j<=n;j++)
    		dp[i][j]=-1e18;
    
    dp[0][0]=0;
    
    for(int i=1;i<=m;i++){
    	deque<int> q;
    	q.push_back(0);
    	for(int j=1;j<=n;j++){
    		while(!q.empty() and j-q.front()>k) q.pop_front();
    		dp[i][j]=dp[i-1][q.front()]+w[j];
    		while(!q.empty() and dp[i-1][j]>=dp[i-1][q.back()]) q.pop_back();
    		q.push_back(j);
    	}	
    }
    LL ans = -1;
    for(int i=n-k+1;i<=n;i++) ans = max(ans,dp[m][i]);
    cout << ans << endl;
    return ;
}

常见的dp从n^3-->n^2都是依靠转移的状态是不是只有上一层,然后通过记录上一层的信息来做直接转移而优化一层循环

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值