4.17集训题解

本次集训随机选取了CF上的3道题,难度在1700-1800分。

899D

题目链接

http://codeforces.com/problemset/problem/899/D

题目

给定n个数,要求从中任取两个数,使得两数和的末尾的9尽量的多。在保证末尾9最多的情况下,问可以凑出多少对。(一个数可以反复使用)

题解

观察得知,当n=5时,可以凑出1个9
当n=50时,可以凑出2个9,(50+49=99)
所以我们先判断一下最多能凑出的9的个数,然后再枚举首位数字计算。
注意:当9的个数为0(n<=4)的时候,任意两个数组合都满足条件。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

ll ans, a[20], n, k = 10;

ll pow(ll a, ll b) {
    ll res = 1;
    while (b) {
        if (b & 1) res *= a;
        a *= a;
        b >>= 1;
    }
    return res;
}

int main() {
    for (int i = 1; i <= k; i++) {
        ll f = 0;
        for (int j = 0; j < i; j++) f = f * 10 + 1;
        a[i] = 9 * f;
    }
    cin >> n;
	if(n<5) {cout<<n*(n-1)/2<<endl;return 0;}
    ll pos = upper_bound(a + 1, a + k + 1, 2 * n - 1) - a;
    pos--;
    ll f = 0;
    for (int i = 0; i < pos; i++) f = f * 10 + 1;
	f *= 9;
	for(int i=0;i<9;i++) {
		ll m = i*pow(10,pos) + f;
		if(m>n*2-1) break;
		// cout << min(m/2,n-m/2) << endl;
		ans += min(m/2,n-m/2);
	}
	cout << ans << endl;
    return 0;
}

898D

题目链接

http://codeforces.com/problemset/problem/898/D

题目

给定n个数,每个数表示一个时间点。要求在任意连续m分钟,出现的时间点的个数<k,要问最小需要删除的时间点的个数。

题解

每次贪心的往后找,删除不满足区间的最右边的端点。

代码

#include <bits/stdc++.h>
using namespace std;

inline void read(int &x) {
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9') {if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
    x *= f;
}

const int maxn = 200005;

int ans,n,m,k,b[maxn],a[maxn];

int main() {
    read(n), read(m), read(k);
    for(int i=0;i<n;i++) read(a[i]);
    sort(a,a+n);
    int cnt = 0;
    for(int i=0;i<n;i++) {
        b[++cnt] = a[i];
        if(cnt>=k) {
            if(b[cnt]-b[cnt-k+1]<m) {
                cnt--;
                ans++;
            }
        } 
    }
    cout << ans << endl;
    return 0;
}

888E

题目链接

http://codeforces.com/problemset/problem/888/E

题目

给定n个数(n<=35),要求从中任取若干个数,使得他们的和 mod m的值最大。

题解

对于每个数都有取和不取两种方法,那么直接暴力穷举的话状态就有 2 35 2^{35} 235种,这样必然会超时。

于是我们先枚举前n/2个,再枚举后面n/2个,存在两个集合a,b中。接着对于a中的每一个元素i,枚举b中 <m-i 的最大值,累计到结果。

由于两个<m的数加起来必然<2*m, 所以需要将两个集合中最大的元素加起来,和之前的结果比较大小,选一个最大的。

代码

#include <bits/stdc++.h>
using namespace std;

constexpr int maxn = 55;

typedef long long ll;

void read(ll &x) {
    ll f=1;x=0;char s=getchar();
    while(s<'0'||s>'9') {if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
    x *= f;
}

ll sum,n,p,a[maxn],m,b[maxn],ans;
vector<ll> e,f;

void dfs(ll x, ll k) {
    if(x>=n/2) {e.push_back(k%p);return;}
    dfs(x+1,k);
    dfs(x+1,k+a[x]);
}

void dfs2(ll x,ll k) {
    if(x>=m) {f.push_back(k%p);return;}
    dfs2(x+1,k);
    dfs2(x+1,k+b[x]);
}

int main() {
    read(n), read(p);
    for(int i=0;i<n;i++) read(a[i]);
    for(int i=n/2;i<n;i++) b[m++] = a[i];
    dfs(0,0);
    dfs2(0,0);
    sort(e.begin(),e.end());
    sort(f.begin(),f.end());
    ans = (e.back()+f.back())%p;
    for(auto i:e) {
        ll k = lower_bound(f.begin(),f.end(),p-i) - f.begin();
        if(k) k--;
        ans = max(ans,(i+f[k])%p);
    }
    cout << ans << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

总想玩世不恭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值