B. so easy
解法:我们离线写这题,每次更新一个x,我们取x,x + 1两个点存着,最后把存的点离散化建线段树,然后每次查询线段树区间最小值即可
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e6 + 10 ;
int mn[ maxn * 4 ] , a[ maxn] , inf = 2e9 ;
#define ls o * 2
#define rs o * 2 + 1
#define m (l + r) / 2
void build ( int o, int l, int r) {
if ( l == r) {
mn[ o] = a[ l] ;
return ;
}
build ( ls, l, m) ;
build ( rs, m + 1 , r) ;
mn[ o] = min ( mn[ ls] , mn[ rs] ) ;
}
void up ( int o, int l, int r, int k) {
if ( l == r) {
mn[ o] = inf;
return ;
}
if ( k <= m)
up ( ls, l, m, k) ;
else
up ( rs, m + 1 , r, k) ;
mn[ o] = min ( mn[ ls] , mn[ rs] ) ;
}
int qu ( int o, int l, int r, int ql, int qr) {
if ( l >= ql && r <= qr)
return mn[ o] ;
int res = inf;
if ( ql <= m)
res = min ( res, qu ( ls, l, m, ql, qr) ) ;
if ( qr > m)
res = min ( res, qu ( rs, m + 1 , r, ql, qr) ) ;
return res;
}
int z[ maxn] , x[ maxn] ;
int main ( ) {
int n, q, sz = 0 ;
scanf ( "%d%d" , & n, & q) ;
for ( int i = 1 ; i <= q; i++ ) {
scanf ( "%d%d" , & z[ i] , & x[ i] ) ;
a[ ++ sz] = x[ i] ;
if ( x[ i] < n)
a[ ++ sz] = x[ i] + 1 ;
}
sort ( a + 1 , a + 1 + sz) ;
sz = unique ( a + 1 , a + 1 + sz) - a - 1 ;
for ( int i = 1 ; i <= q; i++ )
x[ i] = lower_bound ( a + 1 , a + 1 + sz, x[ i] ) - a;
build ( 1 , 1 , sz) ;
for ( int i = 1 ; i <= q; i++ )
if ( z[ i] == 1 )
up ( 1 , 1 , sz, x[ i] ) ;
else {
int ans = qu ( 1 , 1 , sz, x[ i] , sz) ;
if ( ans == inf)
ans = - 1 ;
printf ( "%d\n" , ans) ;
}
}
G. Colorful String
解法:我们建立回文树,用bitset存每个回文子串用了哪些字母,然后设dp[i]为以 i 结尾的所有回文子串的所有字母种类数,我们通过fail指针找到 i 的fail节点 j,显然dp[i] = dp[j] + 以 i 结尾的最长回文子串种类数。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 3e5 + 10 ;
ll ans;
int dp[ maxn] ;
bitset< 26 > cat[ maxn] ;
struct ptree{
char s[ maxn] ;
int next[ maxn] [ 26 ] , fail[ maxn] , cnt[ maxn] , len[ maxn] ;
int last, n, p;
long long res;
inline int newnode ( int l) {
cnt[ p] = 0 ;
len[ p] = l;
return p++ ;
}
inline void init ( ) {
newnode ( 0 ) , newnode ( - 1 ) ;
s[ n] = - 1 ;
fail[ 0 ] = 1 ;
}
inline int FL ( int x) {
while ( s[ n- len[ x] - 1 ] != s[ n] ) x= fail[ x] ;
return x;
}
void add ( char c) {
c- = 'a' ;
s[ ++ n] = c;
int cur= FL ( last) ;
if ( ! next[ cur] [ c] ) {
int now= newnode ( len[ cur] + 2 ) ;
cat[ now] = cat[ cur] ;
cat[ now] [ c] = 1 ;
fail[ now] = next[ FL ( fail[ cur] ) ] [ c] ;
next[ cur] [ c] = now;
}
last= next[ cur] [ c] ;
dp[ last] = cat[ last] . count ( ) + dp[ fail[ last] ] ;
ans + = dp[ last] ;
++ cnt[ last] ;
}
} p;
char s[ maxn] ;
int main ( ) {
scanf ( "%s" , s + 1 ) ;
int n = strlen ( s + 1 ) ;
p. init ( ) ;
for ( int i = 1 ; i <= n; i++ )
p. add ( s[ i] ) ;
printf ( "%lld\n" , ans) ;
}
I. query
解法:我们知道排列中所有的gcd(i, j) = min(i, j)的数量在nlogn级别,因此我们可以找到所有 pi 的因子所在的位置,然后根据这个建立可持久化线段树,然后直接查询即可
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 1 ;
int rt[ maxn] , ls[ maxn * 190 ] , rs[ maxn * 190 ] , sum[ maxn * 190 ] , cnt;
int a[ maxn] , p[ maxn] , ans[ maxn] ;
vector< int > G[ maxn] ;
#define mid (l + r) / 2
void up ( int & o, int pre, int l, int r, int k) {
o = ++ cnt;
ls[ o] = ls[ pre] ;
rs[ o] = rs[ pre] ;
sum[ o] = sum[ pre] + 1 ;
if ( l == r)
return ;
if ( k <= mid)
up ( ls[ o] , ls[ pre] , l, mid, k) ;
else
up ( rs[ o] , rs[ pre] , mid + 1 , r, k) ;
}
int qu ( int o, int l, int r, int ql, int qr) {
if ( l >= ql && r <= qr)
return sum[ o] ;
int res = 0 ;
if ( ql <= mid)
res + = qu ( ls[ o] , l, mid, ql, qr) ;
if ( qr > mid)
res + = qu ( rs[ o] , mid + 1 , r, ql, qr) ;
return res;
}
int l[ maxn] , r[ maxn] ;
int main ( ) {
int n, m;
scanf ( "%d%d" , & n, & m) ;
for ( int i = 1 ; i <= n; i++ ) {
scanf ( "%d" , & a[ i] ) ;
p[ a[ i] ] = i;
}
for ( int i = 1 ; i < n; i++ )
for ( int j = i * 2 ; j <= n; j + = i)
if ( p[ i] < p[ j] )
G[ p[ j] ] . push_back ( p[ i] ) ;
for ( int i = 1 ; i <= n; i++ ) {
rt[ i] = rt[ i - 1 ] ;
for ( auto x : G[ i] )
up ( rt[ i] , rt[ i] , 1 , n, x) ;
G[ i] . resize ( 0 ) ;
}
for ( int i = 1 ; i <= m; i++ ) {
scanf ( "%d%d" , & l[ i] , & r[ i] ) ;
ans[ i] + = qu ( rt[ r[ i] ] , 1 , n, l[ i] , n) ;
}
cnt = 0 ;
for ( int i = 1 ; i < n; i++ )
for ( int j = i * 2 ; j <= n; j + = i)
if ( p[ i] > p[ j] )
G[ p[ i] ] . push_back ( p[ j] ) ;
for ( int i = 1 ; i <= n; i++ ) {
rt[ i] = rt[ i - 1 ] ;
for ( auto x : G[ i] )
up ( rt[ i] , rt[ i] , 1 , n, x) ;
G[ i] . resize ( 0 ) ;
}
for ( int i = 1 ; i <= m; i++ )
ans[ i] + = qu ( rt[ r[ i] ] , 1 , n, l[ i] , n) ;
for ( int i = 1 ; i <= m; i++ )
printf ( "%d\n" , ans[ i] ) ;
}
J. Random Access Iterator
解法:正难则反,我们设dp[u]为从u点出发到不了最深的叶子节点概率,设sz[u]为u节点儿子数量,不难想到
d
p
[
u
]
=
(
1
s
z
[
u
]
∑
s
o
n
d
p
[
s
o
n
]
)
s
z
[
u
]
dp[u]=(\frac{1}{sz[u]}\sum_{son}dp[son])^{sz[u]}
d p [ u ] = ( s z [ u ] 1 ∑ s o n d p [ s o n ] ) s z [ u ]
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e6 + 10 , mod = 1e9 + 7 ;
vector< int > G[ maxn] ;
int sz[ maxn] , dep[ maxn] , mx, d[ maxn] ;
void add ( int & x, int y) {
x + = y;
if ( x >= mod)
x - = mod;
if ( x < 0 )
x + = mod;
}
ll ksm ( ll x, int y) {
ll res = 1 ;
while ( y) {
if ( y & 1 )
res = res * x % mod;
x = x * x % mod;
y / = 2 ;
}
return res;
}
void dfs ( int u, int fa) {
dep[ u] = dep[ fa] + 1 ;
mx = max ( mx, dep[ u] ) ;
for ( auto v : G[ u] )
if ( v != fa) {
sz[ u] ++ ;
dfs ( v, u) ;
}
}
int dfs2 ( int u, int fa) {
if ( ! sz[ u] )
return ( mx != dep[ u] ) ;
int inv = ksm ( sz[ u] , mod - 2 ) , ans = 0 ;
for ( auto v : G[ u] )
if ( v != fa)
ans = ( ans + 1ll * inv * dfs2 ( v, u) % mod) % mod;
return ksm ( ans, sz[ u] ) ;
}
int main ( ) {
int n, u, v;
scanf ( "%d" , & n) ;
for ( int i = 1 ; i < n; i++ ) {
scanf ( "%d%d" , & u, & v) ;
G[ u] . push_back ( v) ;
G[ v] . push_back ( u) ;
}
dfs ( 1 , 0 ) ;
int ans = 1 - dfs2 ( 1 , 0 ) ;
add ( ans, 0 ) ;
printf ( "%d\n" , ans) ;
}
M. Longest subsequence
解法:设
d
p
[
i
]
[
c
]
=
p
dp[i][c] = p
d p [ i ] [ c ] = p 为
s
s
s 串前
i
−
1
i - 1
i − 1 个字符,下一次即将匹配
t
t
t 串最大的下标
p
p
p 满足
t
[
p
]
=
c
t[p] = c
t [ p ] = c ,假如
s
[
i
]
=
c
s[i] = c
s [ i ] = c ,那么显然对于所有
j
<
c
j < c
j < c ,
a
n
s
=
m
a
x
(
a
n
s
,
d
p
[
i
]
[
j
]
−
1
+
n
−
i
+
1
)
ans = max(ans, dp[i][j] - 1 + n - i + 1)
a n s = m a x ( a n s , d p [ i ] [ j ] − 1 + n − i + 1 ) ,当然我也可以继续相等的匹配,令
x
=
d
p
[
i
]
[
c
]
+
1
x = dp[i][c] + 1
x = d p [ i ] [ c ] + 1 ,
d
p
[
i
+
1
]
[
t
[
x
]
]
=
m
a
x
(
d
p
[
i
+
1
]
[
t
[
x
]
]
,
d
p
[
i
]
[
x
]
+
1
)
dp[i + 1][t[x]] =max(dp[i + 1][t[x]], dp[i][x] + 1)
d p [ i + 1 ] [ t [ x ] ] = m a x ( d p [ i + 1 ] [ t [ x ] ] , d p [ i ] [ x ] + 1 )
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10 , inf = 1e9 ;
char s[ maxn] , t[ maxn] ;
int dp[ maxn] [ 26 ] ;
int main ( ) {
int n, len;
scanf ( "%d%d" , & n, & len) ;
scanf ( "%s%s" , s + 1 , t + 1 ) ;
int cat = - 1 ;
for ( int i = 0 ; i < 26 ; i++ )
dp[ 0 ] [ i] = - inf;
dp[ 0 ] [ t[ 1 ] - 'a' ] = 1 ;
for ( int i = 1 ; i <= n; i++ ) {
int c = s[ i] - 'a' ;
for ( int j = 0 ; j < 26 ; j++ )
dp[ i] [ j] = dp[ i - 1 ] [ j] ;
for ( int j = c - 1 ; ~ j; j-- )
cat = max ( cat, dp[ i] [ j] - 1 + n - i + 1 ) ;
if ( dp[ i] [ c] == len && i < n)
cat = max ( cat, dp[ i] [ c] - 1 + n - i + 1 ) ;
if ( dp[ i] [ c] != - inf && dp[ i] [ c] < len) {
int x = dp[ i] [ c] + 1 ;
int cc = t[ x] - 'a' ;
dp[ i] [ cc] = max ( dp[ i] [ cc] , dp[ i] [ c] + 1 ) ;
}
}
printf ( "%d\n" , cat) ;
}