感觉最近找到状态了?(反手就是fst)
B1. Cat Party (Easy Edition)
题意:有最多10种颜色,你要求一个最大的x,使得在前x个颜色中移除某个颜色,所有出现过的颜色出现次数相等。
思路:用十个前缀和数组分别保存10种颜色出现次数的前缀和,暴力枚举x,然后记录10种颜色出现次数,然后排个序,如果只有一种颜色 或者 只有一种颜色出现一次其他颜色出现次数相同 或者 只有一种颜色出现k次其他颜色全都出现k-1次,那么把x更新到答案。
#include <bits/stdc++.h>
using namespace std;
const int maxn= 100005 ;
int sum[ 11 ] [ maxn] , a[ 11 ] ;
int ok ( int x)
{
int sz= 0 ;
for ( int i= 1 ; i<= 10 ; i++ )
if ( sum[ i] [ x] != 0 )
{
a[ ++ sz] = sum[ i] [ x] ;
}
sort ( a+ 1 , a+ 1 + sz) ;
if ( sz== 1 )
return 1 ;
if ( a[ 1 ] == 1 && a[ 2 ] == a[ sz] )
return 1 ;
if ( a[ 1 ] == a[ sz- 1 ] && a[ 1 ] == a[ sz] - 1 )
return 1 ;
return 0 ;
}
int main ( )
{
int n, x;
scanf ( "%d" , & n) ;
for ( int i= 1 ; i<= n; i++ )
{
scanf ( "%d" , & x) ;
for ( int j= 1 ; j<= 10 ; j++ )
sum[ j] [ i] = sum[ j] [ i- 1 ] ;
sum[ x] [ i] ++ ;
}
int ans= 0 ;
for ( int i= n; i; i-- )
if ( ok ( i) )
{
ans= i;
break ;
}
cout<< ans;
}
B2. Cat Party (Hard Edition)
题意:同上,颜色数量改为至多1e5
思路:,用一颗权值线段树记录每种颜色出现次数的最大值和最大值 x 最大值数量,最小值和最小值 x 最小值数量,然后根据这四个信息用上个题的判断方法即可。
#include <bits/stdc++.h>
using namespace std;
const int maxn= 100005 ;
int mx[ maxn* 4 ] , mn[ maxn* 4 ] , sum1[ maxn* 4 ] , sum2[ maxn* 4 ] ;
void up ( int o, int l, int r, int k)
{
if ( l== r)
{
mx[ o] ++ ;
mn[ o] = mx[ o] ;
sum1[ o] ++ ;
sum2[ o] ++ ;
return ;
}
int m= ( l+ r) / 2 , ls= o* 2 , rs= o* 2 + 1 ;
if ( k<= m)
up ( ls, l, m, k) ;
else
up ( rs, m+ 1 , r, k) ;
if ( mx[ ls] > mx[ rs] )
mx[ o] = mx[ ls] , sum1[ o] = sum1[ ls] ;
else if ( mx[ ls] < mx[ rs] )
mx[ o] = mx[ rs] , sum1[ o] = sum1[ rs] ;
else
mx[ o] = mx[ ls] , sum1[ o] = sum1[ ls] + sum1[ rs] ;
if ( ! mn[ ls] )
mn[ ls] = 1000000 ;
if ( ! mn[ rs] )
mn[ rs] = 1000000 ;
if ( mn[ ls] < mn[ rs] )
mn[ o] = mn[ ls] , sum2[ o] = sum2[ ls] ;
else if ( mn[ ls] > mn[ rs] )
mn[ o] = mn[ rs] , sum2[ o] = sum2[ rs] ;
else
mn[ o] = mn[ ls] , sum2[ o] = sum2[ ls] + sum2[ rs] ;
}
set< int > s;
int ok ( int n)
{
if ( s. size ( ) == 1 )
return 1 ;
if ( sum1[ 1 ] == n&& mx[ 1 ] == 1 )
return 1 ;
if ( sum2[ 1 ] == 1 && sum1[ 1 ] + sum2[ 1 ] == n)
return 1 ;
if ( mx[ 1 ] == mn[ 1 ] + 1 && sum1[ 1 ] == mx[ 1 ] && sum1[ 1 ] + sum2[ 1 ] == n)
return 1 ;
return 0 ;
}
int main ( )
{
int n, x, ans= 0 ;
scanf ( "%d" , & n) ;
for ( int i= 1 ; i<= n; i++ )
{
scanf ( "%d" , & x) ;
up ( 1 , 1 , 100000 , x) ;
s. insert ( x) ;
if ( ok ( i) )
ans= i;
}
cout<< ans;
}
D. Mysterious Code
题意:给你一个c串,你可以用任意小写字母替换c串中的 * 字符得到一个c`,给你一个s和t串,定义:f(s1,s2) 为字符串 s2 在 s1 中出现的次数 ,求最大的 ans=f(c`,s) - f(c`,t)。
题意: 设d[ i ][ j ][ k ]为c`串前 i 个字符匹配到了 s 的第 j 个字符,t 的第 k 个字符所能得到的最大ans,对于 c[i+1]串,我们分两类,如果c[i+1] == ‘*’,那么我们枚举26个字母,使用kmp算每个字母在 s 串和 t 串新的匹配的位置 p ,q,记flag=0,如果枚举的这个字母到了 s 串的末尾,flag+=1,如果到了 t 串的末尾,flag-=1,然后更新d[i+1][p][q]=max(d[i+1][ p ][ q ], d[ i ][ j ][ k ]+flag),如果c[ i+1 ] != ‘*’,那么只要计算c[ i+1 ]得到新的匹配就行了。
#include <bits/stdc++.h>
using namespace std;
const int maxn= 1005 , inf= 1e8 ;
int d[ maxn] [ 55 ] [ 55 ] ;
char s[ 55 ] , t[ 55 ] , c[ maxn] ;
int Next1[ 55 ] , Next2[ 55 ] ;
void init ( )
{
int i, j, len= strlen ( s) ;
Next1[ 0 ] = - 1 ;
for ( i= 0 , j= - 1 ; i< len; )
if ( j== - 1 || s[ i] == s[ j] )
Next1[ ++ i] = ++ j;
else
j= Next1[ j] ;
len= strlen ( t) ;
Next2[ 0 ] = - 1 ;
for ( i= 0 , j= - 1 ; i< len; )
if ( j== - 1 || t[ i] == t[ j] )
Next2[ ++ i] = ++ j;
else
j= Next2[ j] ;
}
void up ( int & x, int y)
{
x= max ( x, y) ;
}
int get1 ( int k, char a)
{
while ( 1 )
{
if ( s[ k] == a|| k== - 1 )
return k+ 1 ;
else
k= Next1[ k] ;
}
}
int get2 ( int k, char a)
{
while ( 1 )
{
if ( t[ k] == a|| k== - 1 )
return k+ 1 ;
else
k= Next2[ k] ;
}
}
int main ( )
{
int ans= - inf;
scanf ( "%s%s%s" , c+ 1 , s, t) ;
init ( ) ;
int n= strlen ( c+ 1 ) , m1= strlen ( s) , m2= strlen ( t) ;
for ( int i= 0 ; i<= n; i++ )
for ( int j= 0 ; j< m1; j++ )
for ( int k= 0 ; k< m2; k++ )
d[ i] [ j] [ k] = - inf;
d[ 0 ] [ 0 ] [ 0 ] = 0 ;
for ( int i= 0 ; i< n; i++ )
for ( int j= 0 ; j< m1; j++ )
for ( int k= 0 ; k< m2; k++ )
if ( d[ i] [ j] [ k] != - inf)
{
int p, q, flag= 0 ;
if ( c[ i+ 1 ] != '*' )
{
p= get1 ( j, c[ i+ 1 ] ) ;
q= get2 ( k, c[ i+ 1 ] ) ;
if ( p== m1)
flag++ , p= Next1[ p] ;
if ( q== m2)
flag-- , q= Next2[ q] ;
up ( d[ i+ 1 ] [ p] [ q] , d[ i] [ j] [ k] + flag) ;
}
else
{
for ( int a= 'a' ; a<= 'z' ; a++ )
{
flag= 0 ;
p= get1 ( j, a) ;
q= get2 ( k, a) ;
if ( p== m1)
flag++ , p= Next1[ p] ;
if ( q== m2)
flag-- , q= Next2[ q] ;
up ( d[ i+ 1 ] [ p] [ q] , d[ i] [ j] [ k] + flag) ;
}
}
}
for ( int i= 0 ; i< m1; i++ )
for ( int j= 0 ; j< m2; j++ )
up ( ans, d[ n] [ i] [ j] ) ;
cout<< ans<< endl;
}