补题计划-牛客周赛Round 69

最近一直很忙没什么时间去写题目,也就是抽空来看了几道题,周二下午没课来集中写了写。

A 题

很简单,学过高中知识就会写。

1
2
3
4
5
6
7
8
9
10
11
#include<bits/stdc++.h>

using namespace std;

int main()
{
int a,b;
cin>>a>>b;
cout<<(a+(a - b))<<endl;
return 0;
}

B 题

和上道题差不多,唯一不同的就是加了一个字符串处理(将string 变成 int)。

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
#include<bits/stdc++.h>

using namespace std;

string s;

long long num(long long l,long long r){
long long base = 1,ans = 0;
for(long long i=r;i>=l;i--){
ans += (s[i] - '0')*base;
base = base * 10;
}
return ans;
}

int main()
{
cin>>s;
long long n = s.length();
long long start = 0,end = 0;
for(long long i=0;i<n;i++){
if(s[i] == ','){
end = i-1;
break;
}
}
long long a = num(start,end);

start = end+2;
for(long long i=end+2;i<n;i++){
if(s[i] == ','){
end = i-1;
break;
}
}

long long b = num(start,end),c = num(end+6,n-1);

long long d = (b - a);

cout<<(c - b) / d - 1<<endl;
return 0;
}

C 题

就是数学加物理题,根据镜面反射的原理,很显然我们需要将 \(z\) 轴的坐标镜面对称。最后同时除以他们的最大公约数就行了。

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
#include<bits/stdc++.h>

using namespace std;

long long n,k;

long long gcd(long long a,long long b){return b == 0 ? a : gcd(b,a%b);}

int main()
{
cin>>n>>k;

for(long long i=1;i<=n;i++){
long long x,y,z;
cin>>x>>y>>z;
z = 2*k - z;

long long t = gcd(gcd(x,y),z);
if(x % t == 0) x = x / t;
if(y % t == 0) y = y / t;
if(z % t == 0) z = z / t;


cout<<x<<" "<<y<<" "<<z<<endl;
}
return 0;
}

D 题

稍微有点难,但是稍微想想也能出来。

看了数据范围为全都 \(\leq 7\),每个计划只有取和不取两种选择,那么我们最多的方案有 \(2^7 = 128\),所以很简单枚举每一种就行了,但是怎么让方案数与每一种的方案关联处理呢?

联系到快速幂,我们可以想到与二进制结合起来。这样就很简单就可以算出来了,剩下的就是循环和判断的事了,对了,题目默认方案之间可以有重叠(错过一次)。

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
#include<bits/stdc++.h>

using namespace std;

const int maxn = 10;
const int maxq = 7;
int t[maxn][maxn],a[maxn][maxn][maxn];

int main()
{
int n,m,q;
cin>>n>>m>>q;
for(int i=0;i<n;i++){
string s;
cin>>s;
for(int j=0;j<m;j++)
t[i][j] = (s[j] - '0');
}

for(int i=0;i<q;i++){
for(int j=0;j<n;j++){
string s;
cin>>s;
for(int k=0;k<m;k++)
a[i][j][k] = (s[k] - '0');
}
}

int anscnt = pow(2,q),ans = -1;
for(int i=0;i<pow(2,q);i++){
bitset<maxq> bits(i);
// cout<<bits<<endl;
int scheme[maxn][maxn],cnt = bits.count();
memset(scheme,0,sizeof(scheme));
for(int j=0;j<q;j++){
if(bits[j] == 1){
for(int x=0;x<n;x++)
for(int y=0;y<m;y++)
scheme[x][y] += a[j][x][y];
}
}

bool flag = 1;
for(int x=0;x<n;x++)
for(int y=0;y<m;y++){
if(t[x][y] == 1 && scheme[x][y] == 0) continue;
else if(t[x][y] == 0 && scheme[x][y] >= 1) continue;
else flag = 0;
}
if(flag == 1)
if(anscnt > cnt) anscnt = cnt,ans = i;
}

if(ans == -1) cout<<ans<<endl;
else{
cout<<anscnt<<endl;
bitset<maxq> bits(ans);
for(int i=0;i<q;i++)
if(bits[i] == 1)
cout<<i+1<<" ";
}
return 0;
}

E 题

说实话,到现在我还没有写出来,感觉是常数太大的问题,感觉时间复杂度没啥太大的问题。感觉极限能过。(突然想到会退化了)

我的想法是,先用前缀和算出前 \(n\) 项和,顺便再记录正数的数字位置,将每个放入 map 中。然后再枚举后面的一个划分点,然后在用 lower_bound() 记录算出区间里有没有正数,就 OK 了。

但是我这个想法会退化,如果数据的许多小区间中,前缀和有相同的,就比如 1 -1 1 -1 1 -1 1 -1, 会退化成 \(O(n)\) 查找每个区间。

所以最坏时间复杂度应该为 \(\Theta (n^2 \log n)\)

平均时间复杂度为 \(O (n \log n \log n)\)

我在夜里突然想到一个很简单的结论,就是划分的三个数组一定是 \(\frac{S}{3}\),那么只需要加上这个判断,那么时间复杂度就会大大下降。wok,这个第一次看我还真没想到这么显然的结论。

那么优化过的最坏时间复杂度下降到 \(\Theta (n \log n)\)

平均时间复杂度为 \(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
#include<bits/stdc++.h>

using namespace std;

const int maxn = 200010;
int a[maxn],sum[maxn];
map<int,vector<int> > m;
vector<int> pos;

int main()
{
int n;
cin>>n;
int cnt = 0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i] > 0) pos.push_back(i);
cnt += a[i];
}

if(cnt % 3 != 0 || pos.size() < 3){cout<<0<<endl;return 0;}

sum[1] = a[1];

for(int i=2;i<=n;i++)
sum[i] = sum[i-1] + a[i];

for(int i=1;i<n-1;i++)
if(sum[i] == cnt / 3)
m[sum[i]].push_back(i);

int ans = 0;
for(int i=n;i>1;i--){
int num = sum[n] - sum[i-1];
if(m.count(num) == 1 && num == cnt / 3){
for(int it : m[num]){
if(i <= it) continue;
int num2 = sum[i-1] - sum[it];
auto a1 = lower_bound(pos.begin(),pos.end(),1);
auto a2 = lower_bound(pos.begin(),pos.end(),it+1);
auto a3 = lower_bound(pos.begin(),pos.end(),i);
if(num2 == num && a1 != pos.end() && a2 != pos.end() && a3 != pos.end()){
if(*a1 <= it && *a2 <= i-1 && *a3 <= n) ans ++;
}
}
}
}

cout<<ans<<endl;
return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!