hihoCoder挑战赛29

先附上官方题解:https://media.hihocoder.com/contests/challenge29/sol.pdf

—————————————————————————–

啊啊啊啊啊啊啊啊啊啊啊啊 ,再次感觉到了自己有多弱!!!!!

1526 : 序列的值

时间限制:20000ms
单点时限:1000ms
内存限制:256MB
描述
给定一个长度为 n 的序列 a[1..n],定义函数 f(b[1..m]) 的值为在 [0,m-1] 内满足如下条件的 i 的数目:

b 中前 i 个数异或起来的值小于 b 中前 i +1个数异或起来的值。

对于 a[1..n] 的每个子序列 b[1..m],求f(b[1..m])之和。

输入
第一行一个正整数 n。

接下来一共有 n 行。第 i+1 行包含一个非负整数 a[i]。

1 ≤ n ≤ 105

0 ≤ a[i] < 231

输出
输出答案对 998244353 取模后的值。

样例输入
2
1
2
样例输出
4


首先考虑a < a^b的情况, 显然需要a中二进制的0位在b中对应位是1且是b的最高位,不懂可以戳这里

然后就是需要对每个数 求贡献,

也就是找每个数 前面所构成在这个数最高位为0的异或和的个数,

可以设一个dp[31][0/1] 代表每一位位0/1的个数

转移就是

if(i1)dp[i][0]=dp[i][0]()+dp[i][1]()dp[i][1]=dp[i][1]()+dp[i][0]()if(i0)dp[i][0]=dp[i][0]()+dp[i][0]()dp[i][1]=dp[i][1]()+dp[i][1]()

以上

附本题代码

int a[N],n;
LL dp[40][2],m[N];

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    LL ans=0;
    for(int i=0;i<=30;i++) dp[i][0]=1;
    m[n]=1;
    for(int i=n-1;i;i--) m[i]=m[i+1]*2%MOD;
    for(int i=1,bt;i<=n;i++){
        bt=0;
        for(int j=30;j>=0;j--)if(a[i]&(1<<j)){bt=j;break;}
        ans=(ans+dp[bt][0]*m[i])%MOD;

        for(int j=0;j<=30;j++){
            int zero,one;
            if(a[i]&(1<<j)){
                zero = (dp[j][0]+dp[j][1]  )%MOD;
                one  = (dp[j][0]+dp[j][1]  )%MOD;
            }
            else {
                zero = (dp[j][0]+dp[j][0]  )%MOD;
                one  = (dp[j][1]+dp[j][1]  )%MOD;
            }
            dp[j][0]=zero,dp[j][1]=one;
        }
    }

    printf("%lld\n",ans%MOD);

    return 0;
}

1527 : 快速乘法

时间限制:20000ms
单点时限:1000ms
内存限制:256MB
描述
在写代码时,我们经常要用到类似 x × a 这样的语句( a 是常数)。众所周知,计算机进行乘法运算是非常慢的,所以我们需要用一些加法、减法和左移的组合来实现乘一个常数这个操作。具体来讲, 我们要把 x × a 替换成: (x<<a0)op1(x<<a1)op2(x<<a2)...opn(x<<an) 这样的形式,其中 opi 是+或者-。

举个例子:x × 15 = (x<<4) - (x<<0)。

在本题中,假设左移(包括左移0)和加法、减法所需要的时间都是一个单位的时间,上述的例子所需要的时间是3。

现在给定常数 a 的二进制形式,求实现 x × a 最少需要多少单位的时间。

输入
一个01串,表示 a 的二进制形式,从左到右分别是从高位到低位。

0 < 01串的长度之和 ≤ 10^6。

a > 0。

输出
输出一个数,表示最小需要多少单位的时间可以实现 x × a。

样例输入
1111
样例输出
3

————————————————————————————————————————
是个二分拆幂 http://www.cnblogs.com/atyuwen/archive/2012/08/04/pow2_partition.html

附本题代码
————————————————————————————————————————

#include <bits/stdc++.h>
typedef long long int LL;
using namespace std;
const int N = 3e6+7;
/*************************************************/

char a[N];
int main(){a[0]='-';
    scanf("%s",a+1);
    int la = strlen(a+1);
    int l=1,r=la;
    while(l<=la&&a[l]=='0') l++;
    while(r&&a[r]=='0') r--;
    if(a[r]=='-') return 0*puts("0");
    int u=1,d=1;
    for(int i=r-1;i>=l;i--){
        if(a[i]=='1') u=min(u,d)+1;
        else          d=min(u,d)+1;
    }
    printf("%d\n",u*2-1);
    return 0;
}

1528 : 投篮比赛

时间限制:40000ms
单点时限:2000ms
内存限制:256MB
描述
有 n 个小朋友在投篮,他们的赛制是这样的:对于每轮,每个人投一颗球,没投进的就淘汰掉,无法参加下轮。特别地,如果所有人都没投进,那就没有人被淘汰。他们将一轮轮比下去直到最后只剩1个人。

现在已知每个小朋友投进的概率是 p,求期望几轮结束比赛。

输入
第一行三个整数 n, x, y。

题目中的 p = x / y。

1 ≤ n ≤ 105

1 ≤ x < y < 998244353

输出
输出答案对 998244353 取模后的值。

分数取模的方法:https://math.stackexchange.com/questions/586595/finding-modular-of-a-fraction

样例输入
2 1 2
样例输出
2
——————————————————————————————————

不会

——————————————————————————————————

1529 : 不上升序列

时间限制:40000ms
单点时限:2000ms
内存限制:256MB
描述
给定一个长度为 n 的非负整数序列 a[1..n]。

你每次可以花费 1 的代价给某个 a[i] 加1或者减1。

求最少需要多少代价能将这个序列变成一个不上升序列。

输入
第一行一个正整数 n。

接下来 n 行每行一个非负整数,第 i 行表示 a[i]。

1 ≤ n ≤ 500000

0 < a[i] ≤ 10^9

输出
一个非负整数,表示答案。

样例解释
[5,3,4,5] -> [5,4,4,4]

样例输入
4
5
3
4
5
样例输出
2

——————————————————————————————————————————
首先这题有个结论, 最后得到的序列中的每一个元素都在原序列中

首先考虑 O(n2) 的dp

有了这个结论,我们可以直接dp

ai 变成第k大的那个序列所需要的最小花费就行了

但是这题数据范围明显需要 O(nlogn)

可以用左偏树降到 O(nlogn) ,然后就上了个左偏树的模板

附本题代码
——————————————————————————————————————————

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <cstdlib>

using namespace std;
typedef long long int LL;
const LL maxn = 500000 + 10;
#define B printf("BUG\n");

LL a[maxn];
LL ans[maxn];

class Node {
public :
    LL v;
    LL h;
    Node * ch[2];
    Node(LL v) : v(v) { ch[0] = ch[1] = NULL; h = 0; }
};

class Seg {
public :
    LL n;
    Node * rt;
    Seg(Node * rt, LL n) : rt(rt), n(n) {}
};

class Leftist {
public :

    stack<Seg *>S;

    Node * merge(Node * t1, Node * t2) {
        if(t1 == NULL) return t2;
        else if(t2 == NULL) return t1;
        if(t1->v < t2->v) swap(t1, t2);
        if(t1->ch[1] == NULL) {
            t1->ch[1] = t2;
            if(t1->ch[0] == NULL) { swap(t1->ch[0], t1->ch[1]); t1->h = 0; }
            else {
                if(t1->ch[0]->h < t1->ch[1]->h) swap(t1->ch[0], t1->ch[1]);
                t1->h = t1->ch[1]->h + 1;
            }
        }
        else {
            t1->ch[1] = merge(t1->ch[1], t2);
            if(t1->ch[0] == NULL) { swap(t1->ch[0], t1->ch[1]); t1->h = 0; }
            else {
                if(t1->ch[0]->h < t1->ch[1]->h) swap(t1->ch[0], t1->ch[1]);
                t1->h = t1->ch[1]->h + 1;
            }
        }
        return t1;
    }

    void solve(LL x) {
        LL num = 1;
        Node * t1 = new Node(x);
        while(!S.empty()) {
            Seg * s2 = S.top();
            if(t1->v < s2->rt->v) {
                LL flag = false;
                if(num % 2 && s2->n % 2) flag = true;
                t1 = merge(t1, s2->rt);
                num += s2->n;
                if(flag) {
                    Node * tmp = t1;
                    t1 = merge(t1->ch[0], t1->ch[1]);
                    delete tmp;
                }
                S.pop();
                delete s2;
            }
            else break;
        }
        Seg * s2 = new Seg(t1, num);
        S.push(s2);
    }

    void removetree(Node * rt) {
        if(rt->ch[0] == NULL && rt->ch[1] == NULL) {
            delete rt;
            rt = NULL;
            return ;
        }
        if(rt->ch[0]) removetree(rt->ch[0]);
        if(rt->ch[1]) removetree(rt->ch[1]);
        delete rt;
        rt = NULL;
    }

    void remove() {
        while(!S.empty()) {
            removetree(S.top()->rt);
            delete S.top();
            S.pop();
        }
    }
};

int main() {
    LL n;
    while(scanf("%lld", &n) != EOF) {
        Leftist lt;
        for( LL i=0; i<n; i++ ) {
            scanf("%lld", &a[i]);
            lt.solve(a[i]);
        }

        LL k = 0;
        while(!lt.S.empty()) {
            Seg * f = lt.S.top(); lt.S.pop();
            for( LL i=0; i<f->n; i++ ) {
                ans[k++] = f->rt->v;
            }
        }
        LL res = 0;
        for( LL i=k-1; i>=0; i-- ) res += abs(ans[i] - a[n-i-1]);
        lt.remove();
        Leftist lt2;
        for( LL i=0; i<n/2; i++ ) swap(a[i], a[n-i-1]);
        for( LL i=0; i<n; i++ ) lt2.solve(a[i]);
        k = 0;
        while(!lt2.S.empty()) {
            Seg * f = lt2.S.top(); lt2.S.pop();
            for( LL i=0; i<f->n; i++ ) {
                ans[k++] = f->rt->v;
            }
        }
        LL res2 = 0;
        for( LL i=k-1; i>=0; i-- ) res2 += abs(ans[i] - a[n-i-1]);
        lt2.remove();
        printf("%lld\n",res2);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值