AtCoder Beginner Contest 347

前面两道阅读理解直接跳过。

C - Ideal Holidays

大意

一种历法,一周有a+b天,前a天是节假日,其余为工作日。

给定一些计划,输出它们是否都安排在节假日。

思路

最重要的是星期几,因此所有D_i模上a+b

问题被转化为:数轴上有一些点,问能否有一个长度为a的区间覆盖所有点。

先排序,然后枚举左端点,看与最右边的点的距离是否超过 a即可。

注意会爆long long。

代码

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
#define int long long
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int n, a, b;
    cin >> n >> a >> b;
    int tot = a + b;
    vector<int> dis;
    for(int i = 0; i < n; i++){
        int x; cin >> x; x--;
        x %= tot;
        dis.push_back(x);
        dis.push_back(tot + x);
    }
    sort(dis.begin(), dis.end());
    int ans = 1e18;
    for(int i = 0; i < n; i++)
        ans = min(ans, dis[i + n - 1] - dis[i] + 1);
    cout << (ans <= a? "Yes": "No") << endl;
    return 0;
}

D - Popcount and XOR

大意

给定a,b,c,解下列方程组,要求0 \le x,y \le 2^{60},若无解输出-1:
\begin{cases} \operatorname{popcount}(x)=a\\ \operatorname{popcount}(y)=b\\ x\oplus y=c\\ \end{cases}

给定任意一组解均可。

思路

cnt=\operatorname{popcount}(c),那么这cnt个1需要分配到x,y中。

设分配了p个1给xq个1给y,并且p+q=cnt

那么剩下的1需要在xor抵消掉。

易得a-p=b-q,结合p+q=cnt可解得p,q

具体解法为:

v=\dfrac{a+b-cnt}{2} \\ p=a-v \\ q=b-v

其中v就是需要抵消掉的1的数量。

我们逐位遍历c,如果是1,则分配给xy(看p,q的值),如果是0,就同时分配给两个数。

无解条件就是上面的方程没有自然数解,即下面几种情况:

  • a+b < c
  • (a+b-cnt) \mod 2 = 1
  • p<0q<0
  • 还有一种难以言说的情况,需要求出答案后验证一下。

代码

#include<iostream>
#include<cassert>
using namespace std;
#define int long long
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    auto popcount = [&](int x) -> int{
        int ret = 0;
        while (x) {
            ret += x & 1;
            x >>= 1;
        }
        return ret;
    };

    int a, b, c;
    cin >> a >> b >> c;

    int aa = a, bb = b, cc = popcount(c);
    if(cc > a + b || ((a + b - cc) & 1)){
        cout << -1 << endl;
        return 0;
    }
    int over = (a + b - cc) / 2;
    a -= over;
    b -= over;
    if(a < 0 || b < 0){
        cout << -1 << endl;
        return 0;
    }

    int x = 0, y = 0;
    for(int i = 0; i < 60; i++){
        if(c & (1ll << i)){
            if(a){
                x |= (1ll << i);
                a--;
            } else if(b){
                y |= (1ll << i);
                b--;
            }
        }else if(over){
            x |= (1ll << i);
            y |= (1ll << i);
            over--;
        }else assert(0);
    }
    if(popcount(x) != aa || popcount(y) != bb) cout << -1 << endl;
    else cout << x << " " << y << endl;

    return 0;
}

E - Set Add Query

大意

初始时,有一个包含n0的数组A和一个空集S

q次操作,每次操作给定一个x,执行以下动作:

  • 如果x在集合中,那就移除它,否则丢入集合。
  • 对于每个满足1\le i\le ni,如果i \in S,那么a_i加上集合的大小。

执行所有操作后,输出A

思路

朴素的模拟的复杂度是O(nq),显然不可取。

考虑在这个过程,当一个数x在集合中时,它在被移出集合之前,都会对A做贡献。

注意到一个数 x做出的贡献是一个连续的操作区间,贡献值就是这个操作区间的|S|的和。

我们可以维护一个关于操作顺序的|S|的前缀和,当集合中的一个数被移除时,我们就计算它的贡献(前缀和相减)。需要记录x何时放入。

操作完后,对还在集合里的数结算贡献即可。

代码

#include<iostream>
#include<set>
#include<vector>
using namespace std;
#define int long long
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int n, q;
    cin >> n >> q;
    vector<int> pre(1, 0), la(n, 0), ans(n, 0);
    set<int> s;
    for(int i = 1; i <= q; i++){
        int x; cin >> x; x--;
        if(s.count(x)){
            ans[x] += pre[i - 1] - pre[la[x] - 1];
            s.erase(x);
        }else{
            la[x] = i;
            s.insert(x);
        }
        pre.push_back(pre.back() + s.size());
    }
    for(auto &i: s) ans[i] += pre[q] - pre[la[i] - 1];
    for(auto &i: ans) cout << i << " ";
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值