[原创]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](异或上这个数)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](异或上这个数)

以上
附本题代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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)(x<<a_0) op_1 (x<<a_1) op_2 (x<<a_2) ... op_n (x<<a_n) 这样的形式,其中opiop_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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 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)O(n^2)的dp

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

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# 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;
}