[原创]hihoCoder挑战赛29
2017-06-27 14:51:27 Tabris_ 阅读数:239
博客爬取于2020-06-14 22:39:56以下为正文
版权声明:本文为Tabris原创文章,未经博主允许不得私自转载。https://blog.csdn.net/qq_33184171/article/details/73798177
##先附上官方题解: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(对应的数第i位为1)\ dp[i][0]=dp[i]0 +dp[i]1 \ dp[i][1]=dp[i]1 +dp[i]0 \ if(对应的数第i位为0)\ 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<<a_0) op_1 (x<<a_1) op_2 (x<<a_2) ... op_n (x<<a_n)$ 这样的形式,其中$op_i$ 是+或者-。
举个例子: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(n^2)$的dp
有了这个结论,我们可以直接dp
把$a_i$变成第k大的那个序列所需要的最小花费就行了
但是这题数据范围明显需要$O(n\log n)$
可以用左偏树降到$O(n\log n)$,然后就上了个左偏树的模板
附本题代码 ——————————————————————————————————————————
# 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; }