冲刺专题之 \(DP\)

\(T_A\) Helping People

$$codeforces$$

题意

给定一个长为 \(n\) 序列 \(A\) , 其中有 \(q\) 个区间 \([l , r]\) 定义为 \(B_i\) , 对于每一个区间 , 其有 \(P_i\) 的概率使 \(A\) 序列从 \(l\) 到 \(r\) 之间的数增加 \(1\) . 问最后整个区间的最大值的期望。其中 , \(n \le 10^5 , q \le 5 \times 10^3\)

对于 \(\forall B_i , B_j (1 \le i , j \le q 且 i \ne j)\) $B_i \cap B_j = \varnothing 或 B_i \subseteq B_j 或 B_j \subseteq B_i $ (即对于两个区间来说,其不相交或被包含)

样例输入,输出

3 5
1 2 3
1 3 0.500
2 2 0.250
1 2 0.800
1 1 0.120
2 2 0.900
4.465000000

本题为 \(SPJ\) , 只要输出与正解之差 \(\le 10^{-6}\) 即可通过。

题解

一言难尽。

现在我们发现这个区间的性质完全满足建树的条件,于是我们建树,并跑树形 \(DP\)

并设一个超级原点 \(0\) 为 \([1 , n]\) , 其增加的概率为 \(0\)

我们设 \(front_i\) 为区间 \(i\) 的区间最大值。

现在考虑设 \(dp_{i , j}\) 为第 \(i\) 号节点 , 最大值为 \(front_i + j\) 的 \(\bf{概率}\) (本题的期望就是个幌子)

设 \(sum_{i , j}\) 为 \(dp_i\) 数组的前缀和。即 \(\le front_i + j\) 的总概率。

若我们枚举到 \(x\) 号节点 :


第一步

我们现在不考虑 \(x\) 会增加 \(1\) :

设 \(mid_{i , j}\) 数组的意义为第 \(i\) 号节点 , 不考虑 \(i\) 会增加 \(1\) , 最大值为 \(front_i + j\) 的概率 .

得到初步转移:

定义 \(SUM\) 初为 : \(\prod\limits_{z \in son_x}sum[ \ z \ ][ \ front_x + i - front_z \ ]\)

\[mid[ \ x \ ][ \ i \ ] = \sum_{y \in son_x }\frac{ SUM }{ sum[ \ y \ ][ \ front_x + i - front_y \ ] } \times mid[ \ y \ ][ \ front_x + i - front_y \ ] \ \ \ (i \in [ \ 0 \ , \ q \ ])
\]
\[SUM -= \frac{ SUM }{ sum[ \ y \ ][ \ front_x + i - front_y \ ] } \times mid[ \ y \ ][ \ front_x + i - front_y \ ]
\]

(对于每一个 \(y\) 的运算之后 \(SUM\) 进行此次运算 )

当然,如果 \(sum[ \ y \ ][ \ front_x + i - front_y \ ] = 0\) 的话 , 此时直接为 \(0\) (别问,问就是没注意错了 \(114514\) 遍,每次 \(\color{red}{WA}\) \(Test \ 20\) )

考虑此式的正确性:

\(\prod\limits_{z \in son_x}sum[ \ z \ ][ \ front_x + i - front_z \ ]\) : 这个东西意味着 对于 \(x\) 的每一个儿子 , 取的值均 \(\le front_x + i\) 的概率 ;

\(\frac{ SUM }{ sum[ \ y \ ][ \ front_x + i - front_y \ ] }\) : 是不考虑 \(y\) 区间 , 其他区间最大值均 \(\le front_x + i\) 的概率 ;

我们 \(\times mid[ \ y \ ][ \ front_x + i - front_y \ ]\) 保证了整个 \(x\) 区间 最大值为 \(y\) 区间的 \(front_x + i\)

但如果 \(SUM\) 不变的话 , 对于两个区间 \(y , z \in son_x\) 均有最大值是 \(front_x + i\) 的情况时,

那么 \(y , z\) 均为 \(front_x + i\) 时的概率将会被计算两次。

此句减的操作是去重的过程。


第二步

我们进行考虑父区间 \(x\) 对于答案的影响。

我们发现当 \(x\) 的最大值和其某个子区间相同时 , \(i = 1 | \ | 0\) 的时候需要分讨一下 (原因下面提到)

对于 \(i \in [2 \ , \ q]\) 时显然不用再考虑那些被 \(x\) 包含却未被 \(y \in son_x\) 包含的点中有最大值。因为即使 \(x\) 选 , 也只会把上面的点变为 \(front_x + 1\) ,不会变为 \(front_x + i\) .

我们得到这样的式子:

\[dp[ \ x \ ][ \ i \ ] = mid[ \ x \ ][ \ i \ ] \times \left( 1 - P[ \ x \ ] \right) + mid[ \ x \ ][ \ i - 1 \ ] \times P[x]
\]

显然

但是对于 $ i = 1 , i = 0$ 情况 , 我们需要对 \(x\) 区间进行分讨了。

如果 \(x\) 区间的最大值不在其子节点区间里:

如果 \(i = 1\) 时 , \(x\) 区间进行加一时 , 变为 \(front_x + 1\) , 最大值可能是他 , 所以这个就保证了区间最大值为 \(front_x + 1\) , 所以剩下的子区间可选的得到的概率为 :

\[dp[ \ x \ ][ \ 1 \ ] = P[ \ x \ ] \times \sum_{y \in son_x }sum[ \ y \ ][ \ front_x - front_y \ ]
\]

注意你会 \(+ 1\) 的 , 所以不能超出 \(front_x\) .

对于 \(i = 0\) 时 , 因为最大值就在已定了,所以剩下的只要选的不超过 \(front_x\) 就可以了.

\[dp[ \ x \ ][ \ 0 \ ] = \left(1 - P[ \ x \ ]\right) \times \sum_{y \in son_x }sum[ \ y \ ][ \ front_x - front_y \ ]
\]

如果 \(front_x\) 出现在其子区间里:

易得:

\[dp[ \ x \ ][ \ 1 \ ] = mid[ \ x \ ][ \ 0 \ ] \times P[ \ x \ ] + mid[ \ x \ ][ \ 1 \ ] \times \left( 1 - P[ \ x \ ]\right)
\]
\[dp[ \ x \ ][ \ 0 \ ] = mid[ \ x \ ][ \ 0 \ ] \times \left(1 - P[ \ x \ ] \right)
\]

\(code\)

CODE
#include <bits/stdc++.h>
#define int long long
using namespace std ;
const int N = 1e5 + 100 ;
const int M = 5e3 + 100 ;
inline int read() {
int x = 0 , f = 1 ;
char c = getchar() ; while ( c < '0' || c > '9' ) {
f = c == '-' ? -f : f ;
c = getchar() ;
} while ( c >= '0' && c <= '9' ) {
x = x * 10 + c - '0' ;
c = getchar() ;
} return x * f ;
} int st[N][20] , lg[N] ;
inline void build_ST( int n ) {
for ( int i = 2 ; i <= n ; ++ i ) {
lg[i] = lg[i >> 1] + 1 ;
} for ( int j = 1 ; j <= lg[n] ; ++ j ) {
for ( int i = 1 ; i + ( 1 << j ) - 1 <= n ; ++ i ) {
st[i][j] = max( st[i][j - 1] , st[i + ( 1 << ( j - 1 ) )][j - 1] ) ;
}
}
}
inline int MAX_ST( int l , int r ) {
int k = lg[r - l + 1] ;
return max( st[l][k] , st[r - ( 1 << k ) + 1][k] ) ;
} class edge {
public:
int to , next ;
}e[M] ; int head[M] , cnt ; bool vis[M] ;
inline void add( int x , int y ) {
cnt ++ ;
e[cnt].to = y ;
e[cnt].next = head[x] ;
head[x] = cnt ;
} int n , q ;
double dp[M][M] , sum[M][M] ;
class Node {
public:
int l , r , front ;
double p ;
}b[M] ;
bool cmp( Node a , Node b ) {
if ( a.l == b.l ) return a.r > b.r ;
return a.l < b.l ;
} void dfs_find( int x ) {
for ( int i = x + 1 ; i <= q ; ++ i ) {
if ( b[x].r < b[i].l ) break ; if ( b[x].l <= b[i].l && b[i].r <= b[x].r && !vis[i] ) {
vis[i] = 1 ;
add(x,i) ; dfs_find(i) ;
}
}
} void dfs( int x ) {
bool flag = 0 ;
for ( int i = head[x] ; i ; i = e[i].next ) {
dfs(e[i].to) ; if ( b[x].front == b[e[i].to].front ) flag = 1 ;
}
long double sum0 = 1 ; bool flak = 0 ; for ( int i = 0 ; i <= q ; ++ i ) {
long double sumi = 1 ; flak = 0 ; for ( int j = head[x] ; j ; j = e[j].next ) {
int y = e[j].to ; if ( b[x].front + i - b[y].front <= q )
if ( sum[y][b[x].front + i - b[y].front] > 0 )
sumi *= sum[y][b[x].front + i - b[y].front] ;
else flak = 1 ;
} if ( !flak )
for ( int j = head[x] ; j ; j = e[j].next ) {
int y = e[j].to ; if ( b[x].front + i - b[y].front <= q )
if ( sum[y][b[x].front + i - b[y].front] > 0 ) {
dp[x][i] += sumi / sum[y][b[x].front + i - b[y].front] * dp[y][b[x].front + i - b[y].front] ;
sumi -= sumi / sum[y][b[x].front + i - b[y].front] * dp[y][b[x].front + i - b[y].front] ;
}
}
} for ( int i = q ; i >= 2 ; -- i ) {
dp[x][i] = dp[x][i - 1] * b[x].p + dp[x][i] * ( 1 - b[x].p ) ;
} if ( !flag ) {
long double sum_0 = 1 ;
for ( int i = head[x] ; i ; i = e[i].next ) {
int y = e[i].to ; if ( b[x].front - b[y].front <= q ) {
sum_0 *= sum[y][b[x].front - b[y].front] ;
}
} dp[x][1] = ( 1 - b[x].p ) * dp[x][1] + b[x].p * sum_0 ;
dp[x][0] = ( 1 - b[x].p ) * sum_0 ;
}
else {
dp[x][1] = dp[x][0] * b[x].p + dp[x][1] * ( 1 - b[x].p ) ;
dp[x][0] = dp[x][0] * ( 1 - b[x].p ) ;
} sum[x][0] = dp[x][0] ;
for ( int i = 1 ; i <= q ; ++ i ) {
sum[x][i] = sum[x][i - 1] + dp[x][i] ;
}
} inline void Query_Expect() {
long double ans = 0 ;
int maxl = b[0].front ; for ( int i = 0 ; i <= q ; ++ i ) { ans += ( maxl + i ) * dp[0][i] ;
} printf( "%.9Lf\n" , ans ) ;
} signed main() {
#ifndef ONLINE_JUDGE
freopen( "1.in" , "r" , stdin ) ;
freopen( "1.out", "w" ,stdout ) ;
#endif n = read() ; q = read() ;
for ( int i = 1 ; i <= n ; ++ i ) {
st[i][0] = read() ;
} build_ST( n ) ; for ( int i = 1 ; i <= q ; ++ i ) {
b[i].l = read() ; b[i].r = read() ;
b[i].front = MAX_ST( b[i].l , b[i].r ) ;
scanf( "%lf" , &b[i].p ) ;
} sort( b + 1 , b + q + 1 , cmp ) ;
b[0].l = 1 , b[0].r = n , b[0].p = 0 ; vis[0] = 1 ;
b[0].front = MAX_ST( 1 , n ) ; dfs_find(0) ; dfs(0) ; Query_Expect() ;
}

\(T_B\) Bird

十分简单的背包,看代码

CODE
#include <bits/stdc++.h>
#define int long long
using namespace std ;
const int N = 1e4 + 100 ;
const int M = 1e3 + 10 ; int dp[N] ;
int up[M][N] , n , b , x , w ;
int num[N] , cost[N] ;
int total[N] ; inline int read() {
int x = 0 , f = 1 ;
char c = getchar( ) ; while ( c < '0' || c > '9' ) { if ( c == '-' ) f = -f ; c = getchar( ) ;
} while ( c >= '0' && c <= '9' ) {
x = x * 10 + c - '0' ;
c = getchar( ) ;
} return x * f ;
} signed main() { #ifndef ONLINE_JUDGE
freopen( "1.in" , "r" , stdin ) ;
freopen( "1.out", "w", stdout ) ;
#endif n = read() , w = read() , b = read() , x = read() ; for ( int i = 1 ; i <= n ; ++ i ) {
num[i] = read() ; total[i] = total[i - 1] + num[i] ;
}
for ( int i = 1 ; i <= n ; ++ i ) {
cost[i] = read() ;
} memset( dp , 128 , sizeof(dp) ) ;
dp[0] = w ; for ( int i = 1 ; i <= n - 1 ; ++ i ) {
for ( int j = total[i] ; j >= 0 ; -- j ) {
for ( int k = 0 ; k <= num[i] && j - k >= 0 ; ++ k ) { if ( dp[j - k] >= 0 && dp[j - k] - cost[i] * k >= 0 ) { dp[j] = max( min( dp[j - k] - cost[i] * k + x , j * b + w ) , dp[j] ) ; }
}
}
}
for ( int j = total[n] ; j >= 0 ; -- j ) {
for ( int k = 0 ; k <= num[n] && j - k >= 0 ; ++ k ) { if ( dp[j - k] >= 0 && dp[j - k] - cost[n] * k >= 0 )
dp[j] = max( min( dp[j - k] - cost[n] * k , j * b + w ) , dp[j] ) ; }
} for ( int i = total[n] ; i >= 0 ; -- i ) { if ( dp[i] >= 0 ) {
cout << i ;
return 0 ;
} }
}

\(T_C\) Positions in Permutations

来看这里

$$my \ blogs$$

\(T_H\) Tavas in Kansas

$$codeforces$$

题意

nishishui123 和 HANGRY_Sol 玩游戏( 原神 ) 给定一个 \(m\) 条边带权无向图 , 边权给定。 HANGRY_Sol在 \(S\) , nishishui123在 \(t\) , HANGRY_Sol 开始,选定一个数 \(x\) ,使得目前操作的人的获得分数加等于 \(dist_i \le x\) 的点的点权。选过一个点后,其权值消失。对于每次操作,至少获得一个点。求最后谁赢。

\(T_I\) Game on Sum

看这里

$$my \ blogs$$

冲刺专题之字符串

前置知识:SA

$$my \ blogs$$

\(T_A\) Security

$$codeforces$$

注意

\(\Huge{\color{red}代码超长警告(虽然没啥必要)}\)

简化题意

现给定一个目标串 \(s\) ,给一堆模式串 \(t_i\) ,求 \(s_{l_i} \dots s_{r_i}\) 这一段字符串中字典序最小且大于 \(t_i\) 字典序的串,并输出他。 ( \(1 \le i \le q\) )

数据范围: $ |s| \le 10^5 , q \le 2 \times 10^5 , \sum\limits t_i \le 2 \times 10^5$

题解

md 费劲死了终于打完了

非常显然的是将所有的查询直接扔进目标串后面,然后直接跑 \(SA\) .

我们发现的是,由于那个非常显眼的 \(\sum\) , 我们就可以枚举这个前缀长度。

我们要前缀最长。

最后用一点二维数点求区间最小值,找出那个位置,最后输出即可。

这题是平凡题(不是我说的,神说的)

\(Code\)

CODE
#include <bits/stdc++.h>
using namespace std ;
const int N = 5e5 + 10 ;
const int Q = 5e5 + 10 ;
inline int read() {
int x = 0 , f = 1 ;
char c = getchar() ; while ( c < '0' || c > '9' ) {
if ( c == '-' ) f = -f ; c = getchar() ;
} while ( c >= '0' && c <= '9' ) {
x = x * 10 + c - '0' ; c = getchar() ;
} return x * f ;
}
inline int max(int a , int b) {
return a > b ? a : b ;
}
inline int min(int a , int b) {
return a < b ? a : b ;
} int tr[N << 4] ; int Being_Subtracted[Q] ; #define lson (id << 1)
#define rson (id << 1 | 1) void Update_Point( int id , int l , int r , int x , int v ) {
if ( l == r ) {
tr[id] = v ;
return ;
} int mid = (l + r) >> 1 ; if ( mid >= x ) Update_Point( lson , l , mid , x , v ) ;
else Update_Point( rson , mid + 1 , r , x , v ) ; tr[id] = min(tr[lson] , tr[rson]) ;
} int Min_Of_Interval_On_Tree( int id , int l , int r , int x , int y ) {
if ( x <= l && r <= y ) {
return tr[id] ;
} int mid = (l + r) >> 1 , ans = 11451419 ; if ( mid >= x ) ans = min( ans , Min_Of_Interval_On_Tree(lson , l , mid , x , y) ) ;
if ( y >= mid + 1 ) ans = min( ans , Min_Of_Interval_On_Tree(rson , mid + 1 , r , x , y) ) ; return ans ;
} #undef lson
#undef rson int n , q ; int fir[Q] , len[Q] ; int lg[Q + N] , ST[Q + N][19] ;
char s[N + Q] ; int height[Q + N] ; char Pres[Q] ; int Zero[Q] ; int maxlong ;
int cnt[N + Q] , SA[N + Q] , Rank[N + Q] , Lstrk[N + Q] , id[N + Q] , key1[N + Q] , p , m = 127 ; int Times ;
class Node {
public:
int x , y ; Node() {
x = y = 0 ;
}
} a[Q] ; vector <Node> v[Q + N] ; class Number {
public:
int Lnum , Rnum ; Number() {
Lnum = Rnum = 0 ;
}
} d[Q] , d2[Q] , d0[Q] , d3[Q] ; class Query_Affair {
public:
int x , y , id , Only_One , Zero_Only ;
Query_Affair() {
x = y = id = Only_One = Zero_Only = 0 ;
}
} b[Q << 4] ; class Query_ {
public:
int a1 , a2 , a3 , a4 , Be_Sub ; Query_() {
a1 = a2 = a3 = a4 = 0 ;
}
} c[Q] , c2[Q] , c3[Q] ; int Answer[Q] ; bool cmp( Query_Affair alpha , Query_Affair beta ) {
if ( alpha.x == beta.x ) {
if ( alpha.y == beta.y ) return alpha.id < beta.id ; return alpha.y < beta.y ;
} return alpha.x < beta.x ;
} int Two_numbol , Smaller[Q] , point[Q] ;
int What_Can_I_Say[Q] , What_Can_I_Say2[Q] ; inline void build_ST() {
for ( int j = 1 ; j <= lg[n] ; ++ j ) {
for ( int i = 1 ; i <= n - (1 << j) + 1 ; ++ i ) {
ST[i][j] = min( ST[i][j - 1] , ST[i + (1 << (j - 1))][j - 1] ) ;
}
}
}
inline int MinST( int l , int r ) {
int k = lg[r - l + 1] ; return min(ST[l][k] , ST[r - (1 << k) + 1][k]) ;
}
inline int Query( int l , int r ) {
if ( l == r ) return n - l + 1 ; return Rank[l] < Rank[r] ? MinST(Rank[l] + 1 , Rank[r]) : MinST( Rank[r] + 1 , Rank[l] ) ;
} namespace BINARY_INDEX_TREE {
int t[10 * N] ; #define lowbit(pos) pos & - pos inline void clear() {
for ( int i = 0 ; i <= n ; ++ i ) t[i] = 0 ;
} void Insert_Times( int pos , int val ) {
while ( pos <= val ) {
++ t[pos] ;
pos += lowbit(pos) ;
}
} int Querier_Tree( int pos ) {
int ans = 0 ; while ( pos > 0 ) {
ans += t[pos] ;
pos -= lowbit(pos) ;
} return ans ;
}
} using namespace BINARY_INDEX_TREE ; bool check(int mid , int lens , int beginer) {
if ( Query(beginer , SA[mid]) < lens ) {
return true ;
} else
return false ;
} signed main() {
#ifndef ONLINE_JUDGE
freopen("1.in" , "r" , stdin) ;
freopen("1.out" , "w" , stdout ) ;
#endif
cin >> s + 1 ; n = strlen(s + 1) ; len[0] = n ; fir[0] = 1 ; int TTT = - clock() ; q = read() ;
int fghjjhgfghjhg = q ;
for ( int i = 1 ; i <= q ; ++ i ) {
a[i].x = read() ; a[i].y = read() ; scanf( "%s" , Pres + 1 ) ;
fir[i] = n + 1 ; len[i] = strlen( Pres + 1 ) ; for ( int j = 1 ; j <= len[i] ; ++ j ) {
s[++ n] = Pres[j] ;
}
} for ( int i = 2 ; i <= n ; ++ i ) {
lg[i] = lg[i >> 1] + 1 ;
} auto compare = [](int x , int y , int j) {
return Lstrk[x] == Lstrk[y] && Lstrk[x + j] == Lstrk[y + j] ;
} ; for ( int i = 1 ; i <= n ; ++ i ) { Rank[i] = s[i] ; ++ cnt[Rank[i]] ; }
for ( int i = 1 ; i <= m ; ++ i ) { cnt[i] += cnt[i - 1] ; }
for ( int i = n ; i >= 1 ; -- i ) { SA[cnt[Rank[i]] --] = i ; } for ( int j = 1 ; ; j <<= 1 , m = p ) { p = 0 ;
for ( int i = n ; i > n - j ; -- i ) id[++ p] = i ;
for ( int i = 1 ; i <= n ; ++ i ) {
if ( SA[i] - j > 0 ) id[++ p] = SA[i] - j ;
} memset( cnt , 0 , sizeof(cnt) ) ; for ( int i = 1 ; i <= n ; ++ i ) { ++ cnt[key1[i] = Rank[id[i]]] ; }
for ( int i = 1 ; i <= m ; ++ i ) { cnt[i] += cnt[i - 1] ; }
for ( int i = n ; i >= 1 ; -- i ) { SA[cnt[key1[i]] --] = id[i] ; } memcpy( Lstrk , Rank , sizeof(Rank) ) ;
p = 0 ; for ( int i = 1 ; i <= n ; ++ i ) {
Rank[SA[i]] = compare( SA[i] , SA[i - 1] , j ) ? p : ++ p ;
} if ( p == n ) {
break ;
}
} fir[q + 1] = n + 1 ; for ( int i = 1 , k = 0 ; i <= n ; ++ i ) {
if ( !Rank[i] ) continue ; if ( k ) k -- ; while ( s[i + k] == s[SA[Rank[i] - 1] + k] ) ++ k ; ST[Rank[i]][0] = height[i] = k ;
} build_ST() ; for ( int i = 1 ; i <= len[0] ; ++ i ) {
b[i].id = 0 ; b[i].x = i , b[i].y = Rank[i] ;
} Two_numbol = len[0] ; for ( int i = 1 ; i <= q ; ++ i ) {
int R = Rank[fir[i]] + 1 , Left = Rank[fir[i]] + 1 , Right = n ; while ( Left <= Right ) {
int mid = Left + Right >> 1 ; if ( !check(mid , 1 , fir[i]) ) {
Left = mid + 1 ;
R = mid ;
} else {
Right = mid - 1 ;
}
} Smaller[i] = R + 1 ; R = Rank[fir[i]] ; for ( int j = fir[i + 1] - 2 ; j >= fir[i] ; -- j ) {
int lcp = j - fir[i] + 1 , left , right , last = R ; if ( R > n ) continue ; int l = a[i].x , r = a[i].y ; if ( r - lcp < l ) continue ;
else left = R + 1 , right = n ; while ( left <= right ) {
int mid = (left + right) >> 1 ; if ( !check(mid , lcp + 1 , fir[i]) ) {
left = mid + 1 ;
R = mid ;
} else {
right = mid - 1 ;
}
} d[j].Lnum = R + 1 ;
left = R + 1 , right = n ; while ( left <= right ) {
int mid = (left + right) >> 1 ; if ( !check(mid , lcp , fir[i]) ) {
left = mid + 1 ;
R = mid ;
} else {
right = mid - 1 ;
}
} d[j].Rnum = R ; if ( d[j].Rnum < d[j].Lnum ) continue ; Two_numbol ++ ; b[Two_numbol].x = l - 1 , b[Two_numbol].y = d[j].Lnum - 1 , b[Two_numbol].id = j , b[Two_numbol].Only_One = Two_numbol ; c[j].a1 = Two_numbol ;
Two_numbol ++ ; b[Two_numbol].x = l - 1 , b[Two_numbol].y = R , b[Two_numbol].id = j , b[Two_numbol].Only_One = Two_numbol ; c[j].a2 = Two_numbol ;
Two_numbol ++ ; b[Two_numbol].x = r - lcp , b[Two_numbol].y = d[j].Lnum - 1 , b[Two_numbol].id = j , b[Two_numbol].Only_One = Two_numbol ; c[j].a3 = Two_numbol ;
Two_numbol ++ ; b[Two_numbol].x = r - lcp , b[Two_numbol].y = R , b[Two_numbol].id = j , b[Two_numbol].Only_One = Two_numbol ; c[j].a4 = Two_numbol ; c[j].Be_Sub = lcp ;
} R = 11451419 ; Left = Rank[fir[i]] + 1 , Right = n ; while ( Left <= Right ) {
int mid = (Left + Right) >> 1 ; if ( !check(mid , len[i] , fir[i]) ) {
Left = mid + 1 ;
R = mid ;
} else {
Right = mid - 1 ;
}
} if ( R != 11451419 && a[i].y - a[i].x + 1 > len[i] && len[i] < a[i].y - a[i].x + 1 ) {
d[fir[i + 1] - 1].Lnum = Rank[fir[i]] + 1 ; d[fir[i + 1] - 1].Rnum = R ; Two_numbol ++ ; b[Two_numbol].x = a[i].x - 1 , b[Two_numbol].y = d[fir[i + 1] - 1].Lnum - 1 , b[Two_numbol].id = fir[i + 1] - 1 , b[Two_numbol].Only_One = Two_numbol ; c[fir[i + 1] - 1].a1 = Two_numbol ;
Two_numbol ++ ; b[Two_numbol].x = a[i].x - 1 , b[Two_numbol].y = R , b[Two_numbol].id = fir[i + 1] - 1 , b[Two_numbol].Only_One = Two_numbol ; c[fir[i + 1] - 1].a2 = Two_numbol ;
Two_numbol ++ ; b[Two_numbol].x = a[i].y - len[i] , b[Two_numbol].y = d[fir[i + 1] - 1].Lnum - 1 , b[Two_numbol].id = fir[i + 1] - 1 , b[Two_numbol].Only_One = Two_numbol ; c[fir[i + 1] - 1].a3 = Two_numbol ;
Two_numbol ++ ; b[Two_numbol].x = a[i].y - len[i] , b[Two_numbol].y = R , b[Two_numbol].id = fir[i + 1] - 1 , b[Two_numbol].Only_One = Two_numbol ; c[fir[i + 1] - 1].a4 = Two_numbol ; c[fir[i + 1] - 1].Be_Sub = len[i] ;
}
} for ( int i = 1 ; i <= q ; ++ i ) {
int lcp = len[i] , l = a[i].x , r = a[i].y , left , right , L = 11451419 ;
right = Rank[fir[i]] - 1 , left = 1 ; if ( !L ) continue ; while ( left <= right ) {
int mid = (left + right) >> 1 ; if ( !check(mid , lcp , fir[i]) ) {
right = mid - 1 ;
L = mid ;
} else {
left = mid + 1 ;
}
} if ( L == 11451419 || r - l + 1 < len[i] ) continue ; d2[i].Lnum = L ; d2[i].Rnum = Rank[fir[i]] - 1 ; if ( d2[i].Lnum > d2[i].Rnum ) continue ; ++ Two_numbol ; b[Two_numbol].x = l - 1 , b[Two_numbol].y = d2[i].Lnum - 1 ; b[Two_numbol].id = i ; b[Two_numbol].Only_One = -1 ; b[Two_numbol].Zero_Only = Two_numbol ; c3[i].a1 = Two_numbol ;
++ Two_numbol ; b[Two_numbol].x = l - 1 , b[Two_numbol].y = d2[i].Rnum ; b[Two_numbol].id = i ; b[Two_numbol].Only_One = -1 ; b[Two_numbol].Zero_Only = Two_numbol ; c3[i].a2 = Two_numbol ;
++ Two_numbol ; b[Two_numbol].x = r - len[i] , b[Two_numbol].y = d2[i].Lnum - 1 ; b[Two_numbol].id = i ; b[Two_numbol].Only_One = -1 ; b[Two_numbol].Zero_Only = Two_numbol ; c3[i].a3 = Two_numbol ;
++ Two_numbol ; b[Two_numbol].x = r - len[i] , b[Two_numbol].y = d2[i].Rnum ; b[Two_numbol].id = i ; b[Two_numbol].Only_One = -1 ; b[Two_numbol].Zero_Only = Two_numbol ; c3[i].a4 = Two_numbol ; c3[i].Be_Sub = len[i] ;
} for ( int i = 1 ; i <= q ; ++ i ) {
if ( Smaller[i] > n ) continue ; d0[i].Lnum = Smaller[i] ; d0[i].Rnum = n ;
Two_numbol ++ ; b[Two_numbol].x = a[i].x - 1 , b[Two_numbol].y = Smaller[i] - 1 ; c2[i].a1 = Two_numbol ; b[Two_numbol].id = i ; b[Two_numbol].Only_One = 0 ; b[Two_numbol].Zero_Only = Two_numbol ;
Two_numbol ++ ; b[Two_numbol].x = a[i].x - 1 , b[Two_numbol].y = n ; c2[i].a2 = Two_numbol ; b[Two_numbol].id = i ; b[Two_numbol].Only_One = 0 ; b[Two_numbol].Zero_Only = Two_numbol ;
Two_numbol ++ ; b[Two_numbol].x = a[i].y , b[Two_numbol].y = Smaller[i] - 1 ; c2[i].a3 = Two_numbol ; b[Two_numbol].id = i ; b[Two_numbol].Only_One = 0 ; b[Two_numbol].Zero_Only = Two_numbol ;
Two_numbol ++ ; b[Two_numbol].x = a[i].y , b[Two_numbol].y = n ; c2[i].a4 = Two_numbol ; b[Two_numbol].id = i ; b[Two_numbol].Only_One = 0 ; b[Two_numbol].Zero_Only = Two_numbol ;
} stable_sort(b + 1 , b + Two_numbol + 1 , cmp) ; for ( int i = 1 ; i <= Two_numbol ; ++ i ) {
if ( b[i].id == 0 ) {
Insert_Times(b[i].y , n) ;
} else {
if ( b[i].Only_One == -1 ) {
if ( c3[b[i].id].a1 == b[i].Zero_Only || c3[b[i].id].a4 == b[i].Zero_Only ) {
What_Can_I_Say2[b[i].id] += Querier_Tree(b[i].y) ;
} else {
What_Can_I_Say2[b[i].id] -= Querier_Tree(b[i].y) ;
}
} else {
if ( b[i].Only_One == 0 ) {
if ( c2[b[i].id].a1 == b[i].Zero_Only || c2[b[i].id].a4 == b[i].Zero_Only ) {
Zero[b[i].id] += Querier_Tree(b[i].y) ;
} else {
Zero[b[i].id] -= Querier_Tree(b[i].y) ;
}
} else {
if ( c[b[i].id].a1 == b[i].Only_One || c[b[i].id].a4 == b[i].Only_One ) {
What_Can_I_Say[b[i].id] += Querier_Tree(b[i].y) ;
} else {
What_Can_I_Say[b[i].id] -= Querier_Tree(b[i].y) ;
}
}
}
}
} Two_numbol = 0 ; for ( int i = 1 ; i <= q ; ++ i ) {
int miner = Zero[i] == 0 ? 11451419 : 0 ;
for ( int j = fir[i + 1] - 1 ; j >= fir[i] ; -- j ) {
if ( What_Can_I_Say[j] ) {
maxlong = max(maxlong , d[j].Rnum - d[j].Lnum + 1) ;
miner = j ;
break ;
}
} if ( What_Can_I_Say2[i] ) miner = -1 ; if ( miner == 11451419 ) {
Answer[i] = -1 ;
} else if ( miner == 0 ) {
++ Two_numbol ; point[i] = Two_numbol ;
What_Can_I_Say2[i] = 0 ; Being_Subtracted[i] = 0 ;
d3[Two_numbol].Lnum = d0[i].Lnum ; d3[Two_numbol].Rnum = d0[i].Rnum ;
} else if ( miner == -1 ) {
++ Two_numbol ; point[i] = Two_numbol ; Being_Subtracted[i] = c3[i].Be_Sub ;
What_Can_I_Say2[i] = len[i] ;
d3[Two_numbol].Lnum = d2[i].Lnum ; d3[Two_numbol].Rnum = d2[i].Rnum ;
} else {
++ Two_numbol ; point[i] = Two_numbol ;
What_Can_I_Say2[i] = miner - fir[i] + 1 ; Being_Subtracted[i] = c[miner].Be_Sub ;
d3[Two_numbol].Lnum = d[miner].Lnum ; d3[Two_numbol].Rnum = d[miner].Rnum ;
}
} Two_numbol = 0 ; for ( int i = 1 ; i <= q ; ++ i ) {
Node alpha ;
alpha.x = i ; alpha.y = 1 ;
if ( Answer[i] != - 1 )
v[d3[point[i]].Lnum].push_back(alpha) ;
} for ( int i = 1 ; i <= len[0] ; ++ i ) {
Update_Point(1 , 1 , len[0] , i , Rank[i]) ;
Node alpha ; alpha.x = i , alpha.y = 0 ;
v[Rank[i]].push_back(alpha) ;
} for ( int i = 1 ; i <= n ; ++ i ) {
for ( int j = 0 ; j < v[i].size() ; ++ j ) {
Node k = v[i][j] ; if ( k.y == 0 ) {
Update_Point(1 , 1 , len[0] , k.x , 0x7f7f7f7f ) ;
} else {
Answer[k.x] = Min_Of_Interval_On_Tree(1 , 1 , len[0] , a[k.x].x , a[k.x].y - Being_Subtracted[k.x] ) ;
}
}
} for ( int i = 1 ; i <= q ; ++ i ) {
if ( Answer[i] == -1 ) printf("-1\n") ;
else {
int first_one = SA[Answer[i]] ; bool flag = 0 ; for ( int j = 0 ; j <= What_Can_I_Say2[i] ; ++ j ) {
putchar(s[first_one + j]) ;
flag = 1 ;
} puts("") ;
}
}
}

\(T_B\) Yet Another LCP Problem

$$codeforces$$

前置

首先要先会一个东西,首先看一下 AHOI2013差异 , 差异luogu

其实要求的只是这个:

\[\sum_{1 \le i < j \le n}LCP\left(T_i , T_j\right)
\]

(\(T_i\) 表示以 \(i\) 为头的后缀 , \(n \le 5 \times 10^5\) )

其实这个题意会误导人,我们换一下:

对于串 \(s\) , 求其中选两个后缀的 \(LCP\) 的和。

虽然两个没任何区别,但是我们就可以打破顺序,按照 \(Rank\) 的顺序进行求解。

这时我们就可以用单调栈来进行求解。

\(top\) 为从 \(i\) (表示的为 \(Rank\) )往上数第一个 < \(height_i\) 的 \(Rank\) 值。

\(dp_i\) 为到 \(i\) 是的贡献值。

st[top]
...
...
i

那从 \(st_{top} + 1\) 到 \(i\) 的 \(height\) 均相等,那这一块的贡献值显然为 \((i - st_{top}) \times height_i\)

可能上面的和 \(i\) 也会有 \(LCP\) 值,所以再加上 \(dp_{st_{top}}\)

核心代码

CODE
for ( int i = 1 ; i <= n ; ++ i ) {
while ( !sta.empty() && height[sta.top()] >= height[i] ) sta.pop() ; dp[i] = (i - sta.top()) * height[i] + dp[sta.top()] ; ans += dp[i] ;
} cout << ans ;

对于本题

给定长度为 \(n\) 的字符串 \(s\) 和 \(q\) 个查询。每个查询是一对整数集合 \(a_1,a_2,\dots,a_k\) 和 \(b_1,b_2,\dots,b_l\) 。计算每个查询的: $$\sum_{i = 1}^{k}\sum_{j = 1}^{l}LCP(T_{a_i} , T_{b_j})$$

题解

考虑将 \(a\) , \(b\) 合成一个 \(c\) , 并将 \(a\) , \(b\) , \(c\) 的任选两个的 \(LCP\) 和全求出来,最后答案为:

\[sum_c - sum_b - sum_a
\]

Code

CODE
#include <bits/stdc++.h>
#define int long long
using namespace std ;
const int N = 2e5 + 10 ;
inline int read() {
int x = 0 , f = 1 ;
char c = getchar() ;
while ( c < '0' || c > '9' ) {
if ( c == '-' ) f = -f ;
c = getchar() ;
}
while ( c >= '0' && c <= '9' ) {
x = x * 10 + c - '0' ;
c = getchar() ;
}
return x * f ;
}
char s[N] ; int height[N] ;
int ST[N][20] , lg[N] ;
int SA[N] , Rank[N] , Lstrk[N] , key1[N] , cnt[N] , m = 127 , n , q , p , id[N] ;
int fir[N] , sec[N] , thi[N << 1] ;
int hei_fir[N] , hei_sec[N] , hei_thi[N << 1] ;
int dp[N << 1] ;
class Monotonic_STACK {
public:
int st[N << 1] , T_POINT ;
inline void push( int x ) {
st[++ T_POINT] = x ;
}
inline void pop() {
-- T_POINT ;
}
inline bool empty() {
return T_POINT ? 0 : 1 ;
}
inline void clear() {
T_POINT = 0 ;
}
inline int top() {
return st[T_POINT] ;
}
Monotonic_STACK() ;
} sta ;
Monotonic_STACK::Monotonic_STACK() {
memset(st , 0 , sizeof(st)) ;
T_POINT = 0 ;
}
inline int max( int a , int b ) {
return a > b ? a : b ;
}
inline int min( int a , int b ) {
return a < b ? a : b ;
}
inline int MinST(int l , int r) {
int k = lg[r - l + 1] ;
return min( ST[l][k] , ST[r - (1 << k) + 1][k] ) ;
}
inline void Build_ST() {
for ( int i = 2 ; i <= n ; ++ i ) {
lg[i] = lg[i >> 1] + 1 ;
}
for ( int j = 1 ; j <= lg[n] ; ++ j ) {
for ( int i = 1 ; i + (1 << j) - 1 <= n ; ++ i ) {
ST[i][j] = min( ST[i][j - 1] , ST[i + (1 << (j -1))][j - 1] ) ;
}
}
}
inline int Query( int l , int r ) {
if ( l == r ) return n - l + 1 ; return Rank[l] < Rank[r] ? MinST(Rank[l] + 1 , Rank[r]) : MinST( Rank[r] + 1 , Rank[l] ) ;
}
signed main() {
#ifndef ONLINE_JUDGE
freopen( "1.in" , "r" , stdin ) ;
freopen( "1.out", "w" ,stdout ) ;
#endif
auto compare = [](int x , int y , int j) {
return Lstrk[x] == Lstrk[y] && Lstrk[x + j] == Lstrk[y + j] ;
} ;
n = read() , q = read() ;
for ( int i = 1 ; i <= n ; ++ i ) {
cin >> s[i] ; cnt[Rank[i] = s[i]] ++ ;
}
for ( int i = 1 ; i <= m ; ++ i ) cnt[i] += cnt[i - 1] ;
for ( int i = n ; i >= 1 ; -- i ) SA[cnt[Rank[i]] --] = i ;
for ( int j = 1 ; ; j <<= 1 , m = p ) {
p = 0 ;
for ( int i = n ; i > n - j ; -- i ) id[++ p] = i ;
for ( int i = 1 ; i <= n ; ++ i ) {
if ( SA[i] - j > 0 ) id[++ p] = SA[i] - j ;
}
memset( cnt , 0 , sizeof(cnt) ) ;
for ( int i = 1 ; i <= n ; ++ i ) cnt[key1[i] = Rank[id[i]]] ++ ;
for ( int i = 1 ; i <= m ; ++ i ) cnt[i] += cnt[i - 1] ;
for ( int i = n ; i >= 1 ; -- i ) SA[cnt[key1[i]] --] = id[i] ;
memcpy( Lstrk , Rank , sizeof(Lstrk) ) ;
p = 0 ;
for ( int i = 1 ; i <= n ; ++ i ) {
if ( compare(SA[i] , SA[i - 1] , j) ) Rank[SA[i]] = p ;
else Rank[SA[i]] = ++ p ;
}
if ( n == p ) break ;
}
for ( int i = 1 , k = 0 ; i <= n ; ++ i ) {
if ( !Rank[i] ) continue ;
if (k) -- k ;
while ( s[i + k] == s[SA[Rank[i] - 1] + k] ) ++ k ;
height[Rank[i]] = k ;
ST[Rank[i]][0] = k ;
}
Build_ST() ;
int x , y , z ;
while ( q -- ) {
x = read() , y = read() ;
for ( int i = 1 ; i <= x ; ++ i ) {
fir[i] = read() ; thi[i] = fir[i] ;
}
for ( int i = 1 ; i <= y ; ++ i ) {
sec[i] = read() ; thi[x + i] = sec[i] ;
}
z = x + y ;
stable_sort( fir + 1 , fir + x + 1 , [](int alpha , int beta) {
return Rank[alpha] < Rank[beta] ;
} ) ;
stable_sort( sec + 1 , sec + y + 1 , [](int alpha , int beta) {
return Rank[alpha] < Rank[beta] ;
} ) ;
stable_sort( thi + 1 , thi + z + 1 , [](int alpha , int beta) {
return Rank[alpha] < Rank[beta] ;
} ) ;
for ( int i = 1 ; i <= x ; ++ i ) hei_fir[i] = Query(fir[i - 1] , fir[i]) ;
for ( int i = 1 ; i <= y ; ++ i ) hei_sec[i] = Query(sec[i - 1] , sec[i]) ;
for ( int i = 1 ; i <= z ; ++ i ) hei_thi[i] = Query(thi[i - 1] , thi[i]) ;
int ans = 0 ;
sta.clear() ;
for ( int i = 1 ; i <= z ; ++ i ) {
while ( !sta.empty() && hei_thi[sta.top()] >= hei_thi[i] ) sta.pop() ;
dp[i] = dp[sta.top()] + (i - sta.top()) * hei_thi[i] ;
ans += dp[i] ;
sta.push(i) ;
}
sta.clear() ;
for ( int i = 1 ; i <= x ; ++ i ) {
while ( !sta.empty() && hei_fir[sta.top()] >= hei_fir[i] ) sta.pop() ;
dp[i] = dp[sta.top()] + (i - sta.top()) * hei_fir[i] ;
ans -= dp[i] ;
sta.push(i) ;
}
sta.clear() ;
for ( int i = 1 ; i <= y ; ++ i ) {
while ( !sta.empty() && hei_sec[sta.top()] >= hei_sec[i] ) sta.pop() ;
dp[i] = dp[sta.top()] + (i - sta.top()) * hei_sec[i] ;
ans -= dp[i] ;
sta.push(i) ;
}
cout << ans << '\n' ;
}
}

\(T_F\) String Distance

$$codeforces$$

本题解来之不易,请大家珍惜。


题意

定义包含两个字符串参数的函数 \(f(a , b)\) 表示将 \(a , b\) 两个字符串任意一个字符串对其某一个子区间进行按字符大小排序,称为一次操作,若 \(a\) 进行 \(\alpha\) 此操作 , \(b\) 进行 \(\beta\) 次操作,使得这两个字符串完全相同,则 \(f(a , b) = min\{\alpha + \beta\}\) , 即进行最少的操作使得变为相同串。

如果不能变为相同的 \(f(a , b) = 1337\)

现在给定 \(n\) 个包含小写字母的、不存在任何两个相同的字符串、长度相同为 \(len\) 的字符串 \(s_1 \dots s_n\) , 求:

\[\sum_{i = 1}^{n}\sum_{j = i + 1}^{n}f(s_i , s_j)
\]

\(n \le 2\times 10^5 , len \le 2 \times 10^5 , n \times len \le 2 \times 10^5\)

题解(根号分治)

首先,显然的如果构成两个字符串的字符不完全相同 , 则一定不能变为相同的。

对于构成两字符串的字符完全相同的两字符串 \(a , b\) 来说 :

如果 \(f(a , b) > 1\) ,我们考虑对每一个字符串选择从 \(1\) 到 \(len\) 的区间都排序,次数为 \(2\) , 两字符串相等。

所以对于两个字符串 \(a , b\) 来说, \(f\) 函数有且仅有 \(3\) 个取值: \(1337 , 1 , 2\) (没有 \(0\) , 因为没有相同串)

如果 \(f(a , b) = 1\) 时满足这种情况:

  • 构成 \(a , b\) 的字符完全相同
  • 存在一段子区间,某一个串在此段为单调不降的,且剩余部分 (前缀和后缀) 两个串完全相等。

我们求出 \(f = 1337\) 的情况,并将由字符的构成的串分为一个个块,对于每个块分别处理

\(n^2 \times len\) 做法

这个 显然 很好想啊,暴力枚举每两个串,求出对应的每个 \(f(s_i , s_j)\) 的值。

时间复杂度为 \(n^2 \times len\) .

\(n \times len^2\) 做法

证明引理

对于两个串,如果存在一个子区间(中间串),任意一个串满足在此子区间中为单调不降趋势串但不是在此位置的最长单调不降趋势串(意思是在头和尾往左或往右都不能再扩展长度),且此子区间再两字符串的补集完全相同(前缀和后缀 , 意思是进行一次交换就可以使两字符串完全相同) 那么将中间串扩展到此位置的单调不降趋势串,依然只用交换一次。

我们可以这么去想:如果次数为 \(1\) , 则前缀和后缀均相同,那扩展到此位置的单调不降趋势串后,前缀和后缀依然相同。所以次数仍为 \(1\) .


我们考虑枚举中间串的首尾,设目前再字符串 \(i\) .

设将其中间串扣去后余下的串的哈希值为 \(Hash_i\) .

记两个哈希表为 \(Hasher1 , Hasher2\) ,

\(Hasher1\) 统计存在多少个哈希值为 \(HASH\) 的,且中间串不为单调不降的串的个数 ;

\(Hasher2\) 表示是否存在一个哈希值为 \(HASH\) 且中间串为此位置最长单调不降串。

  • 如果中间串为单调不降的,但是不是此位置的最长,则跳过,防止记重。

  • 如果不为单调不降的,\(Hasher1_{Hash_i} ++\)

  • 如果为单调不降且为此位置的最长 , \(Hasher2_{Hash_i} = 1\)

最后的答案为:

\[\sum Hasher1_{p[i]} \times Hasher2_{p[i]}
\]

( \(p\) 数组表示此次中间串枚举过程中出现的所有前缀后缀拼起来的串的 \(hash\) 值 )

显然复杂度为 \(n \times len^2\)

组编为代码

考虑什么时候用 \(n^2\times len\) :

我们列一个等式:

$n^2 \times len = n \times len^2 $ , 则\(n = len\)

\(\because n \times len = 2 \times10^5 , \therefore n = 4 \sqrt{5} \times 100\) 时为两种方法切换点。

但因为哈希表比较离谱,所以让 \(n^2 \times len\) 多跑一点

Code

代码里有些错误,我也懒得改了

CODE
#include <bits/stdc++.h>
using namespace std ;
#define int long long
unordered_map <int , int> Hash1 , Hash2 ;
const int down = 26 ;
const int down2 = 25 ;
const int N = 2e5 + 10 ;
const long long Funji = 1.6 * 1e9 ;
#define ULL unsigned long long
ULL does[N] ; ULL does2[N] ;
inline int read() {
int x = 0 , f = 1 ;
char c = getchar() ; while ( c < '0' || c > '9' ) {
if ( c == '-' ) f = -f ; c = getchar() ;
} while ( c >= '0' && c <= '9' ) {
x = x * 10 + c - '0' ;
c = getchar() ;
} return x * f ;
} inline int min( int a , int b ) {
return a < b ? a : b ;
}
inline int max( int a , int b ) {
return a > b ? a : b ;
} vector<int>head[N] , tail[N] ;
vector<ULL>Hasher[N] ;
vector<char> s[N] ;
int Hashp[N] , knumbols ;
long long n , len ;
int duan[N] ; char Reader ;
char s1[N] ; int numbol ;
short SSR[N] ; int length ;
int cnt[N] ; class Node {
public :
int id ;
ULL Hash ; Node() {
id = Hash = 0 ;
}
} a[N] ; inline void Reset() {
for ( int i = 1 ; i <= knumbols ; ++ i ) {
Hashp[i] = 0 ;
} knumbols = 0 ; unordered_map <int , int> ().swap(Hash1) ;
unordered_map <int , int> ().swap(Hash2) ;
} void Search( int ider , int right , int l , int r , int idy ) {
if ( ider == right + 1 ) {
return ;
} int now = a[ider].id ; ULL HASHERI ; if ( l ) HASHERI = Hasher[now][l - 1] + Hasher[now][len - 1] - Hasher[now][r] ;
else HASHERI = Hasher[now][len - 1] - Hasher[now][r] ; if ( head[now][r] - head[now][l] + 1 == r - l + 1 && ( head[now][l] != 1 || ( r != len - 1 && head[now][r + 1] != 1 ) ) ) {
Search( ider + 1 , right , l , r , idy ) ; return ;
} else {
if ( head[now][r] - head[now][l] + 1 == r - l + 1 && head[now][l] == 1 && ( r == len - 1 || head[now][r + 1] == 1 ) ) { Hash2[HASHERI] = 1 ; Search( ider + 1 , right , l , r , idy ) ; return ;
} ++ Hash1[HASHERI] ; if ( Hash1[HASHERI] == 1 ) {
Hashp[++ knumbols] = HASHERI ;
} Search( ider + 1 , right , l , r , idy ) ;
}
} signed main() {
#ifndef ONLINE_JUDGE
freopen( "1.in" , "r" , stdin ) ;
freopen( "1.out" , "w" , stdout ) ;
#endif
long long ans = 0 ; does[0] = 1 ;
for ( int i = 1 ; i <= 2e5 ; ++ i ) {
does[i] = does[i - 1] * down ;
}
does2[0] = 1 ;
for ( int i = 1 ; i <= 2e5 ; ++ i ) {
does2[i] = does2[i - 1] * down2 ;
} n = read() ;
cin >> s1 + 1 ;
len = strlen(s1 + 1) ; for ( int i = 1 ; i <= len ; ++ i ) {
s[1].push_back(s1[i]) ;
s1[i] = 0 ;
} for ( int i = 2 ; i <= n ; ++ i ) {
for ( int j = 1 ; j <= len ; ++ j ) {
cin >> Reader ;
s[i].push_back(Reader) ;
}
} for ( int i = 1 ; i <= n ; ++ i ) {
for ( int j = 0 ; j < len ; ++ j ) {
SSR[j] = s[i][j] - 'a' ;
} stable_sort( SSR + 0 , SSR + len ) ; for ( int j = 0 ; j < len ; ++ j ) {
a[i].Hash += SSR[j] * does2[len - 1 - j] ;
} a[i].id = i ;
} sort( a + 1 , a + n + 1 , [](Node alpha , Node beta) {
return alpha.Hash < beta.Hash ;
} ) ; for ( int i = 1 ; i <= n ; ++ i ) {
if ( a[i].Hash != a[i - 1].Hash || i == 1 ) {
duan[++ numbol] = i ;
}
} duan[numbol + 1] = n + 1 ; int clen = n ; for ( int i = 1 ; i <= numbol ; ++ i ) {
int Count = duan[i + 1] - 1 - duan[i] + 1 ;
ans += Count * ( clen - Count ) * 1337 ;
clen -= Count ;
} if ( (long long)(n * n * len) <= Funji ) { for ( int k = 1 ; k <= numbol ; ++ k ) {
int left = duan[k] , right = duan[k + 1] - 1 ;
int what_can_I_Say = 0 ; for ( int i = left ; i <= right ; ++ i ) {
for ( int j = i + 1 ; j <= right ; ++ j ) {
int fir = a[i].id , sec = a[j].id ;
int front = 0 , back = len - 1 ; while ( s[fir][front] == s[sec][front] && front < len ) {
front ++ ;
} front -- ; while ( back >= 0 && s[sec][back] == s[fir][back] && back != front ) {
back -- ;
} back ++ ; bool flag1 = 0 , flag2 = 0 ; for ( int l = front + 2 ; l <= back - 1 ; ++ l ) {
if ( s[fir][l] < s[fir][l - 1] ) {
flag1 = 1 ;
} if ( s[sec][l] < s[sec][l - 1] ) {
flag2 = 1 ;
}
} if ( !flag1 || !flag2 ) {
what_can_I_Say ++ ;
}
}
} ans += ( ( right - left + 1 ) * ( right - left ) / 2 - what_can_I_Say ) * 2 + what_can_I_Say ;
} cout << ans ; return 0 ;
} else { for ( int i = 1 ; i <= n ; ++ i ) {
for ( int j = 0 ; j < len ; ++ j ) {
if ( j == 0 ) Hasher[i].push_back((s[i][j] - 'a') * does[len - 1]) ;
else {
Hasher[i].push_back(Hasher[i][j - 1] + does[len - 1 - j] * (s[i][j] - 'a')) ;
}
}
} for ( int i = 1 ; i <= n ; ++ i ) {
for ( int j = 1 ; j <= len + 1 ; ++ j ) {
head[i].push_back(0) ;
} head[i][0] = 1 ; for ( int j = 1 ; j < len ; ++ j ) {
if ( s[i][j] < s[i][j - 1] ) {
head[i][j] = 1 ;
} else {
head[i][j] = head[i][j - 1] + 1 ;
}
}
} for ( int k = 1 ; k <= numbol ; ++ k ) {
int numer = 0 ;
int left = duan[k] , right = duan[k + 1] - 1 ; for ( int i = 0 ; i < len ; ++ i ) { // 枚举中间串的左端
for ( int j = i ; j < len ; ++ j ) { // 右端 if ( i == j ) continue ; Reset() ; Search(left , right , i , j , k) ; for ( int l = 1 ; l <= knumbols ; ++ l ) {
numer += Hash1[Hashp[l]] * Hash2[Hashp[l]] ;
}
}
} ans += ( ( duan[k + 1] - 1 - duan[k] + 1 ) * ( duan[k + 1] - 1 - duan[k] ) / 2 - numer ) * 2 + numer ;
} cout << ans ; return 0 ;
}
}

\(T_G\) x-prime Substrings

简化题意:

定义一个数字串 \(s_{l \dots r}\) 的和为 ; \(\sum\limits_{i = l}^{r}s_i\)

对于数字串来说定义一个串是 x - QZS 的,当且仅当:

  1. 这个数字串的和为 \(x\) .

  2. 这个数字串的任意一个真子串和不整除 \(x\) .

现在给定一个目标串 \(s\) ,问你在其中至少删去几个字符才不含有一个 m - QZS 串, \(m\) 给定。

\(|s| \le 1000 , m \le 20\)

题解。

我们发现 m - QZS 串的个数在 \(m = 19\) 时得到最大,但仅有 \(2399\) 个,发现硬排的话显然可以 , 爆 \(DFS\) 很棒。

如果用所有的 m - QZS 串扔进一个 \(ACAM\) 中。题意就变成了这样:

给定一个 \(AC\) 自动机 , 再给定一个目标串 , 问你至少要跳过几个字符,才不到达任何一个有标记的节点。

设 \(dp_{i , j}\) , 表示在目标串上匹配了 \(i\) 个 , 在 \(AC\) 自动机的 \(j\) 号节点。

设其 \(s_i\) 儿子为 \(k\) .

若此边合法,则:

\(dp_{i , k} = dp_{i - 1, j}\)

当然选择直接跳过:

\(dp_{i , k} = dp_{i - 1 , k} + 1\)


这里分析一下一种错误 \(dp\) 方式:

设法一样,就是如果此边不合法:就使其的儿子的值 \(+1\) .

错误之点在于:你是不知道任何其儿子的状态的,而且并不是到了最后时在转眼。

最好的方式是将状态直接存在父亲 \(j\) 上,由其儿子进行转移。

code

CODE
#include <bits/stdc++.h>
#define int long long
using namespace std ;
const int N = 1e3 + 10 ;
inline int read() {
int x = 0 , f = 1 ;
char c = getchar() ; while ( c < '0' || c > '9' ) {
if ( c == '-' ) f = -f ; c = getchar() ;
} while ( c >= '0' && c <= '9' ) {
x = x * 10 + c - '0' ;
c = getchar() ;
} return x * f ;
} char s[N] ; int n , m ;
int cuse[21] , nums , Numer ;
int bebuild[21] , sum[21] ;
int bevis[21] ; int vis[(int)(1e6 + 100)] ;
int dp[2][(int)(1e6 + 100)] ; class AC_Auto_Mation {
public :
struct One_Node {
int son[10] , fail , back , ans ;
} trie[(int)(1e6 + 100)] ; int numbol ; queue<int> q ; void Insert( int Cekas[] , int len ) {
int pos = 1 ; for ( int i = 1 ; i <= len ; ++ i ) {
if ( !trie[pos].son[Cekas[i]] ) {
trie[pos].son[Cekas[i]] = ++ numbol ;
pos = numbol ;
} else {
pos = trie[pos].son[Cekas[i]] ;
}
} trie[pos].back = 1 ;
} void Get_Fail() {
while ( !q.empty() ) {
q.pop() ;
} for ( int i = 1 ; i < 10 ; ++ i ) {
trie[0].son[i] = 1 ;
} q.push(1) ; while ( !q.empty() ) {
int k = q.front() ; q.pop() ; for ( int i = 1 ; i < 10 ; ++ i ) {
int y = trie[k].son[i] , field = trie[k].fail ; if ( !y ) {
trie[k].son[i] = trie[field].son[i] ;
continue ;
} trie[y].fail = trie[field].son[i] ;
q.push(y) ;
}
}
} queue<int> q1 , q2 ; inline void DP() {
while ( !q1.empty() ) {
q1.pop() ;
} while ( !q2.empty() ) {
q2.pop() ;
} q1.push(1) ; for ( int i = 1 ; i <= n ; ++ i ) { for ( int j = 1 ; j <= numbol ; ++ j ) {
dp[(i + 1) & 1][j] = 0x7f7f7f7f ;
} while ( !q1.empty() ) {
vis[q1.front()] = 0 ;
q2.push(q1.front()) ; q1.pop() ;
} while ( !q2.empty() ) {
int k = q2.front() ; q2.pop() ;
int y = trie[k].son[s[i] - '0'] ; if ( !trie[y].back ) {
dp[(i + 1) & 1][y] = min( dp[(i + 1) & 1][y] , dp[i & 1][k] ) ; if ( !vis[y] ) {
vis[y] = 1 ; q1.push(y) ;
}
} dp[(i + 1) & 1][k] = min( dp[(i + 1) & 1][k] , dp[i & 1][k] + 1 ) ; if ( !vis[k] ) {
vis[k] = 1 ;
q1.push(k) ;
}
}
}
} AC_Auto_Mation() {
numbol = 1 ;
memset( trie , 0 , sizeof(trie) ) ;
}
} tree ; void dfs( int val , int pos , int me ) {
if ( val == me ) {
tree.Insert( bebuild , pos - 1 ) ; return ;
} for ( int i = 1 ; i <= Numer ; ++ i ) {
if ( pos != 1 ) { if ( sum[pos - 1] + cuse[i] == me ) {
sum[pos] = sum[pos - 1] + cuse[i] ;
bebuild[pos] = cuse[i] ;
dfs(sum[pos] , pos + 1 , me) ;
continue ;
} bool flag = 0 ; for ( int j = 1 ; j < pos ; ++ j ) {
if ( sum[pos - 1] + cuse[i] > me || !bevis[sum[pos - 1] + cuse[i] - sum[j - 1]] ) {
flag = 1 ; break ;
}
} if ( !flag ) {
sum[pos] = sum[pos - 1] + cuse[i] ;
bebuild[pos] = cuse[i] ;
dfs(sum[pos] , pos + 1 , me) ;
}
} else {
bebuild[pos] = cuse[i] ;
sum[pos] = cuse[i] ;
dfs(sum[pos] , pos + 1 , me) ;
}
}
} signed main() {
#ifndef ONLINE_JUDGE
freopen( "1.in" , "r" , stdin ) ;
freopen( "1.out", "w" ,stdout ) ;
#endif cin >> s + 1 ; n = strlen( s + 1 ) ;
m = read() ; if ( m == 1 ) {
int ans = 0 ; for ( int i = 1 ; i <= n ; ++ i ) {
ans = s[i] == '1' ? 1 + ans : ans ;
} cout << ans << '\n' ; return 0 ;
} for ( int i = 1 ; i <= min( (int)9 , m ) ; ++ i ) {
if ( m % i != 0 ) {
cuse[++ Numer] = i ;
}
} if ( m <= 9 )
cuse[++ Numer] = m ; for ( int i = 1 ; i <= m ; ++ i ) {
if ( m % i != 0 ) {
bevis[i] = 1 ;
}
} dfs( 0 , 1 , m ) ; tree.Get_Fail() ; tree.DP() ; int ans = 0x7f7f7f7f ; for ( int i = 1 ; i <= tree.numbol ; ++ i ) {
ans = min( dp[(n + 1) & 1][i] , ans ) ;
} cout << ans ;
}

\(T_H\) Scissors

$$codeforces$$

题意

给定一个长度为 \(n\) 目标串,给到一个长度为 \(m\) 的模式串 , 你在模式串中按顺序剪出两个不重叠的长度为 \(k\) 的子串,并按 原序 拼在一起 , 输出一种合法的解。

\(k \times 2 \le m \le n \le 5 \times 10^5\)

题解

我好像是第一篇用 \(KMP\) 和 \(ST\) 表混用的题解。

首先我们定义两个数组 \(Matchfront_i\) 和 \(Matchback_i\) .

\(Matchfront_i\) 前 \(i \ ( 1 \le i \le n )\) 个,即由 \(1\) 开始到 \(i\) 的对于目标串的前缀的后缀匹配到模式串的前缀长度。例:

\[abcabcaab(目标串)
\]
\[abcab(模式串)
\]

对于 \(Matchfront_4 = 4\)

\(Matchback_i\) 后 \(i\) 个即由 \(i\) 开始到 \(n\) 的对于目标串的后缀的前缀匹配模式串的后缀长度。例:

还是对于上面的例子,

\(Matchback_8 = 2\)

我们为了满足能剪出两个 \(k\) , 由 \(i = k → n-k+1\)

\(m - Matchfront_i\) 是后面的必有的 \(Matchback_j\)

\(\therefore\) 需枚举。但将 \(j\) 枚举一遍就 \(T\) 了,所以我们拿个 \(ST\) 表存一下其中的最大值。

我们还需要考虑反着的,即

\(m - Matchback_i\) 为 \(Matchfront_j\) 我们还需要判断答案是否合法,具体看代码

CODE
#include <bits/stdc++.h>
#define int long long
using namespace std ;
const int N = 5e5 + 10 ;
const int down = 10 ;
inline int read() {
int x = 0 , f = 1 ;
char c = getchar() ;
while ( c < '0' || c > '9' ) {
if ( c == '-' ) f = -f ; c = getchar() ;
}
while ( c >= '0' && c <= '9' ) {
x = x * 10 + c - '0' ;
c = getchar() ;
}
return x * f ;
}
int n , m , k ; int ST1[N][20] , ST2[N][20] ; int lg[N] ;
char s[N] , t[N] , tf[N] , sf[N] ;
int Next_front[N] , Next_back[N] ;
int Match_front[N] , Match_back[N] ;
inline int lty1( int l , int r ) {
int k0 = lg[r - l + 1] ;
return max( ST1[l][k0] , ST1[r - ( 1 << k0 ) + 1 ][k0] ) ;
}
inline int lty2( int l , int r ) {
int k0 = lg[r - l + 1] ;
return max( ST2[l][k0] , ST2[r - ( 1 << k0 ) + 1 ][k0] ) ;
}
signed main() {
#ifndef ONLINE_JUDGE
freopen( "1.in" , "r" , stdin ) ;
freopen( "1.out" , "w" , stdout ) ;
#endif
n = read() , m = read() , k = read() ;
lg[1] = 0 ;
for ( int i = 2 ; i <= n ; ++ i ) {
lg[i] = lg[i >> 1] + 1 ;
}
cin >> s + 1 ; cin >> t + 1 ;
for ( int i = 1 ; i <= n ; ++ i ) {
sf[i] = s[n - i + 1] ; // 反着存目标串
}
for ( int i = 1 ; i <= m ; ++ i ) {
tf[i] = t[m - i + 1] ; // 反着存模式串
}
for ( int i = 2 , j = 0 ; i <= m ; ++ i ) {
while ( j && t[i] != t[j + 1] ) j = Next_front[j] ; if ( t[i] == t[j + 1] ) j ++ ; Next_front[i] = j ;
}
for ( int i = 2 , j = 0 ; i <= m ; ++ i ) {
while ( j && tf[i] != tf[j + 1] ) j = Next_back[j] ; if ( tf[i] == tf[j + 1] ) j ++ ; Next_back[i] = j ;
}
for ( int i = 1 , j = 0 ; i <= n ; ++ i ) {
while ( j && ( j == m || s[i] != t[j + 1] ) ) j = Next_front[j] ; if ( s[i] == t[j + 1] ) Match_front[i] = ++ j ;
}
for ( int i = 1 , j = 0 ; i <= n ; ++ i ) {
while ( j && ( j == m || sf[i] != tf[j + 1] ) ) j = Next_back[j] ; if ( sf[i] == tf[j + 1] ) Match_back[n - i + 1] = ++ j ;
}
for ( int i = 1 ; i <= n ; ++ i ) {
ST1[i][0] = min( k , Match_back[i] ) ;
ST2[i][0] = min( k , Match_front[i] ) ;
}
for ( int j = 1 ; j <= lg[n] ; ++ j ) {
for ( int i = 1 ; i + ( 1 << j ) - 1 <= n ; ++ i ) {
ST1[i][j] = max( min( ST1[i][j - 1] , k ) , min( ST1[i + ( 1 << ( j - 1 ) )][j - 1] , k ) ) ;
ST2[i][j] = max( min( ST2[i][j - 1] , k ) , min( ST2[i + ( 1 << ( j - 1 ) )][j - 1] , k ) ) ;
}
}
for ( int i = 1 ; i <= k - 1 ; ++ i ) {
if ( Match_front[i] == m ) {
cout << "Yes" << '\n' ;
cout << 1 << ' ' << k + 1 << '\n' ; return 0 ;
}
}
for ( int i = n - k + 1 ; i <= n ; ++ i ) {
if ( Match_front[i] == m ) {
cout << "Yes" << '\n' ;
cout << 1 << ' ' << n - k + 1 << '\n' ; return 0 ;
}
}
for ( int i = k ; i <= n - k ; ++ i ) {
if ( m - min( Match_front[i] , k ) <= lty1( i + 1 , n - k + 1 ) ) {
for ( int j = i + 1 ; j <= n - k + 1 ; ++ j ) {
if ( m - min( Match_front[i] , k ) <= Match_back[j] ) {
if ( Match_front[i] <= k ) {
if ( j + Match_back[j] - m + Match_front[i] <= n - k + 1 ) { // 使达到的位置的字符串能被剪下来
cout << "Yes" << '\n' ;
cout << i - k + 1 << ' ' << j + Match_back[j] - m + Match_front[i] << '\n' ;
} else continue ;
} else {
if ( j + Match_back[j] - m + k <= n - k + 1 ) {// 使达到的位置的字符串能被剪下来
cout << "Yes" << '\n' ;
cout << i - k + 1 << ' ' << j + Match_back[j] - m + k << '\n' ;
} else continue ;
}
return 0 ;
}
}
}
}
for ( int i = k + 1 ; i <= n - k + 1 ; ++ i ) {
if ( m - min( Match_back[i] , k ) <= lty2( k , i - 1 ) ) {
for ( int j = k ; j <= i - 1 ; ++ j ) {
if ( m - min( Match_back[i] , k ) <= lty2( k , i - 1 ) ) {
if ( Match_back[i] <= k ) {
if ( Match_front[j] - m + Match_back[i] >= 1 ) {// 使达到的位置的字符串能被剪下来
cout << "Yes" << '\n' ;
cout << Match_front[j] - m + Match_back[i] << ' ' << i << '\n' ;
} else continue ;
} else {
if ( Match_front[j] - m + k >= 1 ) {// 使达到的位置的字符串能被剪下来
cout << "Yes" << '\n' ;
cout << Match_front[j] - m + k << ' ' << i << '\n' ;
} else continue ;
}
return 0 ;
}
}
}
}
cout << "No" ;
return 0 ;
}

\(T_I\) Minimax

简化题意;

给定字符串中每种字符的个数,让你构造一个数列满足以下条件:

1.对于其所有的前缀求出其 \(border\) 并比对每个的求出最大值定为 \(f(x)\) ,沟造出来的字符串要使其最小。

2.使之字典序最小

\(n \le 10^5\)

题解

说实话看到这个数据范围的时候挺呆的,甚至想枚举首尾的字符,毕竟刚刚不会 \(T\) .

后来发现完全大力分讨即可。

对于分讨情况,给到下面的几个样例:

cjs
ccc
ccccccccccccccccccccccccccccccccccjs
cccjs
cjsc
ccccjs
ccccccjj

1.对于有一个字符只有一个时, cjs , 这时若把此放在头,则 \(f(x) = 0\) 只需将字典序最小的且个数为一的放在最前面,后面的按字典序输出即可。

2.对于只有一个字符的,那只能瞎 jb 输出了,毕竟都一样 悲

3.对于这几种:

cccjs
ccjs
ccccjs

同有一种构造方式:

ccjcs
ccjs
ccjcsc

4.对于有一堆这种一个的 ccccccccccccccccccccccccccccccccccjs

这么构造 : cjcccccccccccccccccccccccccccccccccs

对于个数小于两个的 ccccccjj 这么构造就寄了,可以这么构造:

cjjccccc

所以...代码:

CODE
#include <bits/stdc++.h>
#define int long long
using namespace std ;
const int N = 1e6 + 100 ;
inline int read() {
int x = 0 , f = 1 ;
char c = getchar() ; while ( c < '0' || c > '9' ) {
f = c == '-' ? -f : f ;
c = getchar() ;
} while ( c >= '0' && c <= '9' ) {
x = x * 10 + c - '0' ;
c = getchar() ;
} return x * f ;
} int T , bucket[100] , n , first , numbol ;
char s[N] ; bool flag ;
stack<int>sta ; signed main() {
#ifndef ONLINE_JUDGE
freopen( "1.in" , "r" , stdin ) ;
freopen( "1.out", "w" ,stdout ) ;
#endif auto print = []() {
for ( int i = 0 ; i < 26 ; ++ i ) {
while ( bucket[i] > 0 ) {
bucket[i] -- ;
cout << (char)( i + 'a' ) ;
}
} cout << '\n' ;
} ; auto Charac = [](int Int_) {
return (char)( Int_ + 'a' ) ;
} ; T = read() ; while ( T -- ) {
numbol = 0 ;
memset( bucket , 0 , sizeof(bucket) ) ;
flag = 0 ; first = -1 ; cin >> s + 1 ; n = strlen( s + 1 ) ;
for ( int i = 1 ; i <= n ; ++ i ) {
if ( !bucket[s[i] - 'a'] ) numbol ++ ; bucket[s[i] - 'a'] ++ ;
} if ( n == 0 ) continue ; for ( int i = 0 ; i < 26 ; ++ i ) {
if ( first == -1 && bucket[i] ) {
first = i ;
} if ( bucket[i] == 1 ) {
cout << (char)( i + 'a' ) ;
-- bucket[i] ;
print() ;
flag = 1 ; break ;
} else if ( bucket[i] == n ) {
print() ;
flag = 1 ; break ;
}
} if ( flag == 1 ) continue ; if ( n - ( n - bucket[first] ) * 2 == 2 || ( n - bucket[first] ) * 2 + 1 == n || n == 2 * bucket[first] ) { while ( !sta.empty() ) {
sta.pop() ;
} for ( int i = 25 ; i > first ; -- i ) {
int tmp = bucket[i] ; while ( tmp > 0 ) {
tmp -- ;
sta.push(i) ;
}
} int leav = n - bucket[first] ; cout << Charac(first) << Charac(first) ; bucket[first] -= 2 ;
int tmp = bucket[first] ; for ( int i = 1 ; i <= leav ; ++ i ) {
cout << Charac(sta.top()) ; sta.pop() ; if ( tmp ) {
cout << Charac(first) ; tmp -- ;
}
} cout << '\n' ;
} else if ( numbol != 2 && bucket[first] > (n >> 1) ) {
int tmp_fir = -1 , tmp_sec = -1 ;
for ( int i = first + 1 ; i < 26 ; ++ i ) {
if ( bucket[i] && tmp_fir == -1 ) {
tmp_fir = i ;
} else if ( bucket[i] ) {
tmp_sec = i ; break ;
}
} cout << Charac(first) << Charac(tmp_fir) ;
bucket[tmp_fir] -- ; bucket[first] -- ; for ( int i = 1 ; i <= bucket[first] ; ++ i ) {
cout << Charac(first) ;
} bucket[first] = 0 ;
cout << Charac(tmp_sec) ; bucket[tmp_sec] -- ; print() ;
} else { if ( numbol == 2 && bucket[first] > n - bucket[first] ) {
cout << Charac(first) ; bucket[first] -- ;
int tmp ; for ( int i = first + 1 ; i < 26 ; ++ i ) {
if ( bucket[i] ) {
tmp = i ; break ;
}
} for ( int i = 1 ; i <= bucket[tmp] ; ++ i ) {
cout << Charac(tmp) ;
}
bucket[tmp] = 0 ; print() ;
} else {
cout << Charac(first) ;
bucket[first] -- ; while ( !sta.empty() ) {
sta.pop() ;
} for ( int i = 25 ; i > first ; -- i ) {
int tmp = bucket[i] ; while ( tmp > 0 ) {
tmp -- ;
sta.push(i) ;
}
} for ( int i = 1 ; i <= bucket[first] ; ++ i ) {
cout << Charac(first) << Charac(sta.top()) ; sta.pop() ;
} while ( !sta.empty() ) {
cout << Charac(sta.top()) ; sta.pop() ;
} cout << '\n' ;
} }
}
}

结尾撒花 \(\color{pink}{✿✿ヽ(°▽°)ノ✿}\)

冲刺 NOIP 400pts + 之神仙专题的更多相关文章

  1. 2017/10 冲刺NOIP集训记录:暁の水平线に胜利を刻むのです!

    前几次集训都没有记录每天的点滴……感觉缺失了很多反思的机会. 这次就从今天开始吧!不能懈怠,稳步前进! 2017/10/1 今天上午进行了集训的第一次考试…… 但是这次考试似乎是近几次我考得最渣的一次 ...

  2. 冲刺NOIP复习,算法知识点总结

    前言        离NOIP还有一个星期,匆忙的把整理的算法补充完善,看着当时的整理觉得那时还年少.第二页贴了几张从贴吧里找来的图片,看着就很热血的.当年来学这个竞赛就是为了兴趣,感受计算机之美的. ...

  3. 【dp专题】NOIP真题-DP专题练习

    这里学习一下DP的正确姿势. 也为了ZJOI2019去水一下做一些准备 题解就随便写写啦. 后续还是会有专题练习和综合练习的. P1005 矩阵取数游戏 给出$n \times m$矩阵每次在每一行取 ...

  4. NOIP 2013 花匠 神仙操作

    题目:https://www.luogu.org/problemnew/show/P1970 今天又学习了一个新的神仙操作: 标签是DP,想了一下,没什么心情写,默默打开题解——(狂喜!) 一位大佬( ...

  5. 东北育才冲刺noip(day9)

    这十天来呢,感觉自己进步很大,(虽然被碾压的很惨),看到了自己以前完全没见过,也没想过的算法,打开新世界的大门. 同时呢,也感觉自己太弱了,于是就注册了这个博客. 为了促进进步,在这里立下flag,我 ...

  6. 2021.10.27考试总结[冲刺NOIP模拟17]

    T1 宝藏 发现每个数成为中位数的长度是关于权值单调的.线段树二分判断是否合法,单调指针扫即可. 考场上写了二分,平添\(\log\). \(code:\) T1 #include<bits/s ...

  7. 2021.10.26考试总结[冲刺NOIP模拟16]

    T1 树上的数 \(DFS\)一遍.结构体存边好像更快? \(code:\) T1 #include<bits/stdc++.h> using namespace std; namespa ...

  8. 【游记】NOIP 2017

    时间:2017.11.11~2017.11.12 地点:广东省广州市第六中学 Day1 T1:看到题目,心想这种题目也能放在T1? 这个结论我之前遇到过至少3次,自己也简单证明过.初见是NOIP200 ...

  9. AFO && OI回忆录

    技不如人,甘拜下风 今天是2019.4.6,联考第一天,菜鸡attack原题爆炸(其实是都不会)心灰意冷(其实并没有很难过)写下了这篇文章 T1 2h写个跟\(k\)无关的假算法写到最后发现是三个lo ...

  10. 【基础练习】【拓扑排序】codevs3294 车站分级题解

    题目来源:NOIP2013 普及第四题 题目描写叙述 Description 一条单向的铁路线上,依次有编号为1, 2, -, n的n个火车站.每一个火车站都有一个级别,最低为1级.现有若干趟车次在这 ...

随机推荐

  1. Linux 内核:设备树(4)设备树中各个节点是谁转换的

    Linux 内核:设备树(4)设备树中各个节点是谁转换的 背景 之前,我们在<把device_node转换成platfrom_device>中提到在设备树的device_node到plat ...

  2. python基础-入门必备知识

    1 标识符 标识符是编程时使用的名字,用于给变量.函数.语句块等命名,Python 中标识符由字母.数字.下划线组成,不能以数字开头,区分大小写. 以下划线开头的标识符有特殊含义,单下划线开头的标识符 ...

  3. ARM+DSP异构多核——全志T113-i+玄铁HiFi4核心板规格书

    核心板简介 创龙科技SOM-TLT113是一款基于全志科技T113-i双核ARM Cortex-A7 + 玄铁C906 RISC-V + HiFi4 DSP异构多核处理器设计的全国产工业核心板,ARM ...

  4. 使用sqlcel导入数据时出现“a column named '***' already belongs to this datatable”问题的解决办法

    我修改编码为GBK之后,选择导入部分字段,如下: 这样就不会出现之前的问题了,完美 ----------------------------------------------- 但是出现一个问题,我 ...

  5. Java BigDecimal 算术运算

    算术运算 BigDecimal bignum1 = new BigDecimal("10"); BigDecimal bignum2 = new BigDecimal(" ...

  6. CvT:微软提出结合CNN的ViT架构 | 2021 arxiv

    CvT将Transformer与CNN在图像识别任务中的优势相结合,从CNN中借鉴了多阶段的层级结构设计,同时引入了Convolutional Token Embedding和Convolutiona ...

  7. 共享库soname机制

    目录 前言 共享库版本号 共享库命名机制 realname soname linkname 总结 参考文章 前言 在使用第三方库时,我们会发现第三方库会提供一组文件,他们的后缀一般是.so(如libn ...

  8. 图的存储、创建、遍历、求最小生成树、最短路径(Java)

    带权无向图 存储结构 存储结构选用邻接表. 当一个图为稀疏图时,使用邻接矩阵法显然要浪费大量的存储空间,而图的邻接表法结合了顺序存储和链式存储方法,大大减少了这种不必要的浪费. 当然,即使我们所处理的 ...

  9. [oeasy]python0088_字节_Byte_存储单位_KB_MB_GB_TB

    编码进化 回忆上次内容 上次 回顾了 字符大战的结果 ibm 曾经的 EBCDIC 由于字符不连续的隐患 导致后续 出现 无数问题 无法补救 7-bit 的 ASA X3.4-1963 字母序号连续 ...

  10. 数据分析应该掌握的知识及SQL技能

    一.概念及常识 1.数据分析必备的统计学知识 描述统计学 1.平均值.中位数.众数 2.方差.标准差 3.统计分布:正态分布.指数分布.二项分布.卡方分布 推论统计学 1.假设检验 2.置信区间 3. ...