贪心 + 笛卡尔树,主席树 + 标记永久化,推式子 + 矩阵乘法
啥都不会,只会骗分 QwQ,没有想到的是 T2 预处理, 查询能得 90pts,T3 __int128 竟然存的下 !充分告诉我们要有信仰 qaq
# A. 「2017 山东二轮集训 Day1」第二题
看起来是这套题里面最简单的了。
大力贪心。
每次找到最低点,然后分治处理两边。
答案分别加上两边的大小除以 ,然后多余的部分送给最低点统计。
可以通过建笛卡尔树找最低点。
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
using namespace std;
namespace IO{
inline int read(){
int x = 0;
char ch = getchar();
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x;
}
template <typename T> inline void write(T x){
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace IO;
const int N = 1e5 + 10;
int n, k;
int h[N];
struct Tree{
int l, r;
}t[N];
int stk[N], top, res;
int rt, ans;
int sum[N], siz[N];
inline void dfs(int x, int fa){
siz[x] = 1;
if(ls) dfs(ls, x), sum[x] += sum[ls], siz[x] += siz[ls];
if(rs) dfs(rs, x), sum[x] += sum[rs], siz[x] += siz[rs];
sum[x] += siz[x] * (h[x] - h[fa]);
if(sum[x] > 0){
int tot = ceil(1.0 * sum[x] / k);
ans += tot, sum[x] -= tot * k;
}
}
signed main(){
n = read(), k = read();
for(int i = 1; i <= n; ++i) h[i] = read();
for(int i = 1; i <= n; ++i, res = 0){
while(top && h[stk[top]] > h[i]) res = stk[top--];
t[i].l = res, t[stk[top]].r = i, stk[++top] = i;
}
rt = stk[1];
dfs(rt, 0);
write(ans), puts("");
return 0;
}
// X.K.
# B. 「2017 山东二轮集训 Day1」第一题
毒瘤数据结构。
大致思路是找出每个点向右能到达的最远的点,然后把这个贡献加到主席树里,查询的时候直接在主席树上查询 的区间和即可。
那么如何预处理呢?
设 为前 个数的异或和,最高位 从 和 的个数, 为前缀异或和。
对于区间 ,如果 的最高位 为 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
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
using namespace std;
namespace IO{
inline int read(){
int x = 0;
char ch = getchar();
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x;
}
template <typename T> inline void write(T x){
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace IO;
const int N = 2e5 + 10;
int n, m, lst;
int a[N], pos[N], sum[N];
int s[2][N][66];
inline int highbit(int x){
for(int i = 30; i >= 0; --i) if((x >> i) & 1) return i;
return -1;
}
inline int bit(int x, int y) {return (x >> y) & 1;}
inline bool check(int l, int r){
for(int i = 30; i >= 0; --i){
int x = !bit(sum[l - 1], i);
if(s[x][r][i] - s[x][l - 1][i] > 0) return 0;
}
return 1;
}
inline int find(int x){
int l = x, r = n, res = x;
while(l <= r){
int mid = (l + r) >> 1;
if(check(x, mid)) res = mid, l = mid + 1;
else r = mid - 1;
}
return res;
}
int rt[N], cnt;
struct Tree{
int val, tag, l, r;
}t[N << 5];
inline int build(int l, int r){
int p = ++cnt;
if(l == r) return p;
int mid = (l + r) >> 1;
ls(p) = build(l, mid), rs(p) = build(mid + 1, r);
return p;
}
inline int update(int pre, int L, int R, int k, int l, int r){
int p = ++cnt;
t[p] = t[pre];
if(L == l && R == r) return t[p].tag += k, p;
t[p].val += k * (R - L + 1);
int mid = (l + r) >> 1;
if(L <= mid) ls(p) = update(ls(pre), L, min(R, mid), k, l, mid);
if(R > mid) rs(p) = update(rs(pre), max(L, mid + 1), R, k, mid + 1, r);
return p;
}
inline int query(int x, int y, int L, int R, int l, int r){
int res = (t[y].tag - t[x].tag) * (R - L + 1);
if(L == l && R == r) return res + t[y].val - t[x].val;
int mid = (l + r) >> 1;
if(L <= mid) res += query(ls(x), ls(y), L, min(R, mid), l, mid);
if(R > mid) res += query(rs(x), rs(y), max(L, mid + 1), R, mid + 1, r);
return res;
}
signed main(){
n = read();
for(int i = 1; i <= n; ++i){
sum[i] = sum[i - 1] ^ (a[i] = read());
for(int j = 0; j <= 30; ++j) s[0][i][j] = s[0][i - 1][j], s[1][i][j] = s[1][i - 1][j];
int j = highbit(a[i]);
if(~j) s[bit(sum[i - 1], j)][i][j]++;
}
for(int i = 1; i <= n; ++i) pos[i] = find(i);
rt[0] = build(1, n);
for(int i = 1; i <= n; ++i) rt[i] = update(rt[i - 1], i, pos[i], 1, 1, n);
m = read();
while(m--){
int l = (read() + lst) % n + 1, r = (read() + lst) % n + 1;
if(l > r) swap(l, r);
write(lst = query(rt[l - 1], rt[r], l, r, 1, n)), puts("");
}
return 0;
}
// X.K.
# C. GCD
这道题就相当之困难了。
题目让我们求 。
首先通过更相减损术可以去掉 这一项。
式子就变成了 。
我们把 和 分开计算:
-
。
通过打表发现,,所以就是 。
-
,设 ,然后先除掉这一部分,即求 ,然后让左边模 计算即可。
所以答案就是:
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
using namespace std;
namespace IO{
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
template <typename T> inline void write(T x){
if(x < 0) x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace IO;
const int N = 1e6;
int T, n, p, a, b, c, d;
int mod = 998244353;
struct Matrix{
int num[2][2];
Matrix (){memset(num, 0, sizeof(num));}
inline void init (){num[0][0] = num[0][1] = 1;}
Matrix operator / (int b) const{
Matrix r;
for(int i = 0; i < 2; ++i)
r.num[i][0] = num[i][0] / b, r.num[i][1] = num[i][1] / b;
return r;
}
Matrix operator * (const Matrix &b) const{
Matrix r;
for(int i = 0; i < 2; ++i)
for(int j = 0; j < 2; ++j)
r.num[i][j] = (num[i][0] * b.num[0][j] % mod + num[i][1] * b.num[1][j] % mod) % mod;
return r;
}
Matrix operator ^ (int b) const{
Matrix a, r;
memcpy(a.num, num, sizeof(num));
r.num[0][0] = r.num[1][1] = 1;
for(; b; a = a * a, b >>= 1)
if(b & 1) r = r * a;
return r;
}
}X, X2;
inline int gcd(int a, int b){
return !b ? a : gcd(b, a % b);
}
inline int qpow(int a, int b){
int res = 1;
while(b){
if(b & 1) res = res * a % p;
a = a * a % p, b >>= 1;
}
return res;
}
int t;
inline int f(int n, int p){
if(n <= 1) return 1;
n--, mod = p;
Matrix F;
F.init();
F = F * (X2 ^ t);
n -= (t << 1);
if(n < 0) return F.num[0][1];
if(n > 0) F = F * X;
return F.num[0][0];
}
inline int solve(){
n = read(), p = read(), a = read(), b = read(), c = read(), d = read();
while(d){
int k = b / d;
b -= k * d, a -= k * c;
swap(a, c), swap(b, d);
}
t = n >> 1, mod = p;
int tmp = qpow(3, t);
if(!c) return (a * f(n, p) % p + b * f(n + 1, p) % p) % p * tmp % p;
if(!b) return gcd(a, c) % p * f(n, p) % p * tmp % p;
int g = gcd(f(n, b), b);
mod = g * c;
return gcd((a * f(n, mod) % mod + b * f(n + 1, mod) % mod) % mod / g, c) * g % p * tmp % p;
}
signed main(){
X.num[0][0] = 9, X.num[0][1] = 1, X.num[1][0] = 12;
X2 = X * X / 3;
T = read();
while(T--) write(solve()), puts("");
return 0;
}
// X.K.