文章目录
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/fd3ca491847b836663092f7377b1f0f8.png)
T1:A Twisty Movement
题目
题解
因为 a i a_i ai= 1 / 2 1/2 1/2,于是我们可以确定答案一定是: [ 1 , 1 , 1 , … ] [ 2 , 2 , 2 … ] [ 1 , 1 , 1 … ] [ 2 , 2 , 2 … ] [1,1,1,…][2,2,2…][1,1,1…][2,2,2…] [1,1,1,…][2,2,2…][1,1,1…][2,2,2…]这样的四段子序列(每一段都允许为空)中第二、三段所在区间翻转得到
我们可以从左往右做前缀和 p r e [ i ] pre[i] pre[i]表示 [ 1 , i ] [1,i] [1,i]出现 1 1 1的个数,后缀和 s u f [ i ] suf[i] suf[i]表示 [ i , n ] [i,n] [i,n]出现 2 2 2的个数
然后我们枚举二、三段的分界点 k , k ∈ [ 1 , n + 1 ] k,k∈[1,n+1] k,k∈[1,n+1],再设一、二段的分界点为 p , p ∈ [ 1 , k ] p,p∈[1,k] p,p∈[1,k],三、四段的分界点为 q , q ∈ [ k , n + 1 ] q,q∈[k,n+1] q,q∈[k,n+1]
那么答案为
(
p
r
e
[
p
−
1
]
)
+
(
s
u
f
[
p
]
−
s
u
f
[
k
]
)
+
(
p
r
e
[
q
−
1
]
−
p
r
e
[
k
]
)
+
(
s
u
f
[
q
]
)
(pre[p-1])+(suf[p]-suf[k])+(pre[q-1]-pre[k])+(suf[q])
(pre[p−1])+(suf[p]−suf[k])+(pre[q−1]−pre[k])+(suf[q])
将式子中括号括起来的当为一个整体
这个式子可以化为:
(
p
r
e
[
p
−
1
]
+
s
u
f
[
p
]
+
p
r
e
[
q
−
1
]
+
s
u
f
[
q
]
)
−
(
s
u
f
[
k
]
+
p
r
e
[
k
]
)
(pre[p-1]+suf[p]+pre[q-1]+suf[q])-(suf[k]+pre[k])
(pre[p−1]+suf[p]+pre[q−1]+suf[q])−(suf[k]+pre[k])
p
∈
[
1
,
k
]
,
q
∈
[
k
,
n
+
1
]
p∈[1,k],q∈[k,n+1]
p∈[1,k],q∈[k,n+1]
发现对于一个确定的
k
k
k,我们要最大化前面括号的式子
而第一个括号里面的东西可以用线段树维护下
p
r
e
[
i
−
1
]
+
s
u
f
[
i
]
pre[i-1]+suf[i]
pre[i−1]+suf[i]
那么只用枚举
k
k
k,再两次区间最大值查询,更新答案就好了
code
#include <cstdio>
#include <iostream>
using namespace std;
#define MAXN 2005
int n, result;
int a[MAXN], pre[MAXN], suf[MAXN], tree[MAXN << 2];
void insert ( int t, int l, int r, int id, int val ) {
if ( l == r ) {
tree[t] = val;
return;
}
int mid = ( l + r ) >> 1;
if ( id <= mid )
insert ( t << 1, l, mid, id, val );
else
insert ( t << 1 | 1, mid + 1, r, id, val );
tree[t] = max ( tree[t << 1], tree[t << 1 | 1] );
}
int query ( int t, int l, int r, int L, int R ) {
if ( L <= l && r <= R )
return tree[t];
int mid = ( l + r ) >> 1;
int ans = 0;
if ( L <= mid )
ans = max ( ans, query ( t << 1, l, mid, L, R ) );
if ( mid < R )
ans = max ( ans, query ( t << 1 | 1, mid + 1, r, L, R ) );
return ans;
}
int main() {
scanf ( "%d", &n );
for ( int i = 1;i <= n;i ++ )
scanf ( "%d", &a[i] );
for ( int i = 1;i <= n;i ++ )
pre[i] = pre[i - 1] + ( a[i] == 1 );
for ( int i = n;i >= 1;i -- )
suf[i] = suf[i + 1] + ( a[i] == 2 );
for ( int i = 1;i <= n + 1;i ++ )
insert ( 1, 1, n + 1, i, pre[i - 1] + suf[i] );
for ( int i = 1;i <= n + 1;i ++ ) {
int tmp1 = query ( 1, 1, n + 1, 1, i ), tmp2 = query ( 1, 1, n + 1, i, n + 1 );
result = max ( result, tmp1 + tmp2 - pre[i - 1] - suf[i] );
}
printf ( "%d", result );
return 0;
}
T2:Add Points
题目
题解
这道题实在不知道怎么讲,我觉得直接扔一句话就懂了
排完序后两两距离的最大公约数
code
#include <cstdio>
#include <algorithm>
using namespace std;
#define MAXN 100005
int n;
int x[MAXN];
int gcd ( int x, int y ) {
if ( ! y )
return x;
else
return gcd ( y, x % y );
}
int main() {
scanf ( "%d", &n );
for ( int i = 1;i <= n;i ++ )
scanf ( "%d", &x[i] );
sort ( x + 1, x + n + 1 );
int d = x[2] - x[1];
for ( int i = 2;i < n;i ++ ) {
int dis = x[i + 1] - x[i];
d = gcd ( d, dis );
}
int result = 0;
for ( int i = 1;i < n;i ++ )
result += ( x[i + 1] - x[i] ) / d - 1;
printf ( "%d", result );
return 0;
}
T3:The Monster
题目
题解
其实还是有点意思,我认为比
T
2
T2
T2要难一点
首先直接纯暴力
2
5000
2^{5000}
25000可以直接当场去世了
考虑枚举起点,用
t
o
t
tot
tot记录,
(
(
(就
+
1
+1
+1,
)
)
)就
−
1
-1
−1,
若
t
o
t
=
=
0
tot == 0
tot==0,说明匹配成功
r
e
s
u
l
t
+
+
result++
result++
对于
?
?
?就麻烦一点了
t
o
t
>
0
tot>0
tot>0,表示需要右括号匹配,使
?
?
?为右括号,
因为右括号一定可以改为左括号(期待后面能够匹配成功),
因此
c
h
a
n
g
e
+
+
change++
change++(
c
h
a
n
g
e
change
change为可以修改为左括号的
?
?
?个数)
t
o
t
<
0
tot<0
tot<0,
?
?
?只能为左括号,并且不能计入num
如果某一个时刻, t o t < 0 , n u m > 0 tot<0,num>0 tot<0,num>0,说明可以将之前的右括号改为左括号,则 t o t + = 2 , n u m − − tot += 2,num-- tot+=2,num−−
剪枝部分:若 t o t < 0 , n u m = 0 tot<0,num=0 tot<0,num=0,说明此序列当前及以后都不会合法,直接 b r e a k break break
code
#include <cstdio>
#include <cstring>
#define MAXN 5005
char s[MAXN];
int result, tot, change;
int main() {
scanf ( "%s", s );
int len = strlen ( s );
for ( int i = 0;i < len;i ++ ) {
tot = 0, change = 0;
for ( int j = i;j < len;j ++ ) {
if ( s[j] == '(' )
tot ++;
else if ( s[j] == ')' )
tot --;
else {
if ( tot > 0 )
tot --, change ++;
else
tot ++;
}
if ( tot < 0 && change > 0 )
tot += 2, change --;//°Ñ֮ǰµ±³É')'ÌṩµÄ-1¼Ó»ØÀ´ÔÙ¼ÓÉÏ'('µÄ¹±Ï×
if ( tot < 0 && ! change )
break;
if ( ! tot )
result ++;
}
}
printf ( "%d", result );
return 0;
}
T4:Congruence Equation
题目
题解
对于这种数论题只想说一句:不康题解:这神马玩意儿;康完题解:太水了
n
∗
a
n
≡
b
(
m
o
d
p
)
n*a^n≡b(mod\ p)
n∗an≡b(mod p),加上费马那一堆人的鬼定理
我们知道第一个乘数
n
n
n的循环节是
p
p
p
而指数
n
n
n的循环节是
φ
(
p
)
φ(p)
φ(p),又因为
p
p
p为质数,所以
φ
(
p
)
=
p
−
1
φ(p)=p-1
φ(p)=p−1
综上
n
⋅
a
n
n⋅a^n
n⋅an有循环节
p
∗
(
p
−
1
)
p*(p−1)
p∗(p−1)
然后就可以设
n
=
j
∗
(
p
−
1
)
+
i
n=j*(p-1)+i
n=j∗(p−1)+i,接着开始搞事
n
∗
a
n
≡
b
(
m
o
d
p
)
n*a^n≡b(mod\ p)
n∗an≡b(mod p)
(
j
∗
(
p
−
1
)
+
i
)
∗
a
j
∗
(
p
−
1
)
+
i
≡
b
(
m
o
d
p
)
(j*(p-1)+i)*a^{j*(p-1)+i}≡b(mod\ p)
(j∗(p−1)+i)∗aj∗(p−1)+i≡b(mod p)
(
j
∗
p
−
j
+
i
)
∗
a
i
≡
b
(
m
o
d
p
)
(j*p-j+i)*a^i≡b(mod\ p)
(j∗p−j+i)∗ai≡b(mod p)
(
i
−
j
)
∗
a
i
≡
b
(
m
o
d
p
)
(i-j)*a^i≡b(mod\ p)
(i−j)∗ai≡b(mod p)
j
≡
i
−
b
a
i
(
m
o
d
p
)
j≡i-\frac{b}{a^i}(mod\ p)
j≡i−aib(mod p)
然后就可以枚举
i
i
i解出
j
,
n
j,n
j,n搞到一个最小解,根据循环节算有几个
x
−
m
i
n
n
p
⋅
(
p
−
1
)
\frac{x−minn}{p⋅(p−1)}
p⋅(p−1)x−minn
code
#include <cstdio>
#define int long long
int a, b, p, x, result;
int qkpow ( int x, int y ) {
int ans = 1;
while ( y ) {
if ( y & 1 )
ans = ans * x % p;
x = x * x % p;
y >>= 1;
}
return ans;
}
signed main() {
scanf ( "%lld %lld %lld %lld", &a, &b, &p, &x );
for ( int i = 1;i < p;i ++ ) {
int j = ( i - b * qkpow ( qkpow ( a, i ), p - 2 ) % p + p ) % p;
int minx = j * ( p - 1 ) + i;
if ( minx <= x )
result += ( x - minx ) / ( p * ( p - 1 ) ) + 1;
}
printf ( "%lld", result );
return 0;
}