题目链接:https://ac.nowcoder.com/acm/contest/3005
题解链接:https://ac.nowcoder.com/discuss/365889?type=101&order=0&pos=5&page=1
A - 欧几里得
反欧几里德,根据1、0来求相应的最小原始数,可以直接进行欧几里德算法的逆运算
也可以打表找规律,发现答案是斐波那契数列:0、1、1、2、3、5、8…
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
int main(void)
{
int t;
ll n, x, y;
cin >> t;
while (t--){
cin >> n;
x = 1; y = 0;
while (n--){
swap(x, y);
while (x <= y){
x += y;
}
}
cout << x + y << endl;
}
return 0;
}
B - 括号序列
遍历字符串,如果是左半部分就进栈
如果是右半部分且栈不为空就进行匹配,能匹配上就使当前栈顶元素出栈,匹配不上就使其进栈。
最后如果栈为空说明合法,栈不为空说明不合法
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e6 + 5;
char st[N];
int main(void)
{
int t = 0;
string s;
cin >> s;
int len = s.size();
for (int i = 0; i < len; i++){
if( t > 0 && s[i] == ')' && st[t] == '(') t--;
else if (t > 0 && s[i] == '}' && st[t] == '{') t--;
else if (t > 0 && s[i] == ']' && st[t] == '[') t--;
else st[++t] = s[i];
}
if (t > 0) cout << "No" << endl;
else cout << "Yes" << endl;
return 0;
}
C - 子段乘积
本题可以使用线段树,也可以分治,还可以使用尺取法
尺取法做法:维护当前区间中有几个0,同时维护不是0的数字的乘积。因为是除法的取模,所以这种方法需要使用乘法逆元。
时间复杂度为 O ( n ) O(n) O(n)
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 2e5 + 5, M = 998244353;
typedef long long ll;
int arr[N];
ll mod_pow(ll x, ll n, ll mod)
{
ll res = 1;
while (n > 0){
if (n & 1) res = res * x % mod;
x = x * x % mod;
n >>= 1;
}
return res % mod;
}
int main(void)
{
int n, k, s;
int l, r, num;
ll sum, ans, cnt;
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i++)
scanf("%d", &arr[i]);
//初始化
l = 0; r = 0;
sum = 1;
ans = -1;
cnt = 0;
num = 0;
while (true)
{
//右端点右移
while (r < n && num < k)
{
if (arr[r])
sum = (sum * arr[r]) % M;
else
cnt++;
r++;
num++;
}
//遍历结束
if (r >= n)
break;
//更新答案
if (cnt == 0)
ans = max(sum, ans);
else
ans = max((ll)0, ans);
//左端点右移
if (arr[l] != 0)
sum = (sum % M) * mod_pow(arr[l], M - 2, M) % M;
else
cnt--;
l++;
num--;
}
if (ans == -1)//不存在区间和大于s的区间
printf("0\n");
else
printf("%d\n", ans);
return 0;
}
线段树解法
直接枚举区间的乘积就可以,复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 2e5 + 5, M = 998244353;
typedef long long ll;
struct node{
int l, r;
ll val, lazy;
}t[N << 2];
int n, m, l, r;
char c[2];
ll ans, maxv;
void push_up(int x)//更新和值(建树、修改)
{
t[x].val = (t[x * 2].val * t[x * 2 + 1].val) % M;
}
void build(int x, int l, int r)//建树
{
t[x].l = l; t[x].r = r;
if (l == r){
scanf("%lld", &t[x].val);
return;
}
int mid = (l + r) >> 1;
build(x * 2, l, mid);
build(x * 2 + 1, mid + 1, r);
push_up(x);
}
ll query(int x, int l, int r)//区间查询
{
if (t[x].l == l && t[x].r == r)
return t[x].val;
//push_down(x);
int mid = (t[x].l + t[x].r) >> 1;
if (r <= mid)
return query(x * 2, l, r) % M;
else if (l > mid)
return query(x * 2 + 1, l, r) % M;
else
return (query(x * 2, l, mid) % M) * (query(x * 2 + 1, mid + 1, r) % M) % M;
}
int main(void)
{
int n, k;
scanf("%d%d", &n, &k);
build(1, 1, n);
for (int i = 1; i <= n + 1 - k; i++){
maxv = query(1, i, i + k - 1) % M;
ans = max(ans, maxv);
}
printf("%lld\n", ans);
return 0;
}
D - 子段异或
如果 [ l , r ] [l, r] [l,r]异或和为0,则说明 x o r s u m [ l − 1 ] ⨁ x o r s u m [ r ] = 0 xorsum[l-1]\bigoplus xorsum[r]=0 xorsum[l−1]⨁xorsum[r]=0,即 x o r s u m [ l − 1 ] = x o r s u m [ r ] xorsum[l-1]=xorsum[r] xorsum[l−1]=xorsum[r],
所以只需要求出所有的前缀和,用 m a p map map记录每个前缀和有多少个等于它即可,还需要注意前0个数的前缀和等于0,需要额外记录一次。
#include <cstdio>
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
typedef long long ll;
map<ll, ll> m;
map<ll, ll>::iterator it;
int main(void)
{
ll n, t, x = 0;
ll ans = 0;
scanf("%lld", &n);
m[0] = 1;
for (int i = 1; i <= n; i++){
scanf("%lld", &t);
x ^= t;
m[x]++;
}
for (it = m.begin(); it != m.end(); it++){
n = it -> second;
ans += n * (n - 1) / 2;
}
printf("%lld\n", ans);
return 0;
}
F - 树上博弈
当两人间的距离为偶数时,总是牛牛胜。否则总是牛妹胜。
因为每次两人走完之后,距离的奇偶不变,所以只需要统计深度为奇数和偶数的节点个数
用异或1将其深度转化为0或1来表示奇数和偶数,再统计0、1的个数即可
两人同为奇数深度和同为偶数深度的情况即为答案
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;
int depth[N];
ll cnt[2];
int main(void)
{
int n, t;
scanf("%d", &n);
depth[1] = 0;
cnt[0] = 1;
for (int i = 2; i <= n; i++){
scanf("%d", &t);
depth[i] = depth[t] ^ 1;
cnt[depth[i]]++;
}
ll ans = cnt[0] * (cnt[0] - 1) + cnt[1] * (cnt[1] - 1);
printf("%lld\n", ans);
return 0;
}