说在前面
Emmmmmm,每次好不容易想出来解法,实现上却总是各种疏忽,该拿的分拿不满,很气。
题目&&题解
T1
这个题好像是没有什么针对的数据结构的,但是其实可以用一个线段树就把这个题水了,因为字母一共只有26种,我们可以直接查询每种数字的个数,然后一段一段的区间修改。如果是升序,从A到Z依次赋值,降序就从Z到A,常数比较大但是可以加一些比如读优和register一样的玄学优化
可以卡过就对了嘛=w=
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
int N , M ;
char ss[100005] ;
struct Node{
int cnt[26] , flag ;
Node *ls , *rs ;
Node(){
memset( cnt , 0 , sizeof( cnt ) ) ;
}
void pushdown( int lf , int rg ){
if( flag != -1 ){
int mid = ( lf + rg ) >> 1 ;
for( register int i = 0 ; i < 26 ; i ++ )
ls->cnt[i] = rs->cnt[i] = 0 ;
ls->cnt[ flag ] = ( mid - lf + 1 ) ;
rs->cnt[ flag ] = ( rg - mid ) ;
ls->flag = rs->flag = flag ;
flag = -1 ;
}
}
void updata(){
for( register int i = 0 ; i < 26 ; i ++ )
cnt[i] = ls->cnt[i] + rs->cnt[i] ;
}
}w[200005] , *root , *tw = w ;
inline int read_(){
int rt = 0 ;
char ch = getchar() ;
while( ch < '0' || ch > '9' ) ch = getchar() ;
while( ch >='0' && ch <='9' ) rt = (rt<<1) + (rt<<3) + ch - '0' , ch = getchar() ;
return rt ;
}
Node *build( int lf , int rg ){
Node *nd = ++tw ;
if( lf == rg ) nd->cnt[ ss[lf]-'a' ] = 1 ;
else{
int mid = ( lf + rg ) >> 1 ;
nd->ls = build( lf , mid ) ;
nd->rs = build( mid+1 , rg ) ;
nd->updata() ; nd->flag = -1 ;
}
return nd ;
}
void Modify( Node *nd , int lf , int rg , int L , int R , int delta ){
if( L <= lf && rg <= R ){
nd->flag = delta ;
for( int i = 0 ; i < 26 ; i ++ )
nd->cnt[i] = 0 ;
nd->cnt[delta] = ( rg - lf + 1 ) ;
return ;
}
int mid = ( lf + rg ) >> 1 ;
nd->pushdown( lf , rg ) ;
if( L <= mid ) Modify( nd->ls , lf , mid , L , R , delta ) ;
if( R > mid ) Modify( nd->rs , mid+1, rg , L , R , delta ) ;
nd->updata() ;
}
Node Query( Node *nd , int lf , int rg , int L , int R ){
if( L <= lf && rg <= R ) return *nd ;
else{
Node rt = Node() , tmp ;
int mid = ( lf + rg ) >> 1 ;
nd->pushdown( lf , rg ) ;
if( L <= mid ){
tmp = Query( nd->ls , lf , mid , L , R ) ;
for( register int i = 0 ; i < 26 ; i ++ )
rt.cnt[i] += tmp.cnt[i] ;
}
if( R > mid ){
tmp = Query( nd->rs , mid+1 , rg , L , R ) ;
for( register int i = 0 ; i < 26 ; i ++ )
rt.cnt[i] += tmp.cnt[i] ;
}
return rt ;
}
}
void print( Node *nd , int lf , int rg ){
if( lf == rg ){
for( int i = 0 ; i < 26 ; i ++ )
if( nd->cnt[i] ){
printf("%c" , i + 'a' ) ;
return ;
}
}
int mid = ( lf + rg ) >> 1 ;
nd->pushdown( lf , rg ) ;
print( nd->ls , lf , mid ) ;
print( nd->rs , mid+1 , rg ) ;
}
void solve(){
for( register int i = 1 , x , L , R ; i <= M ; i ++ ){
L = read_() ; R = read_() ; x = read_() ;
int st = L ;
Node tmp = Query( root , 1 , N , L , R ) ;
switch( x ){
case 1:{
for( register int j = 0 ; j < 26 ; j ++ )
if( tmp.cnt[j] ){
Modify( root , 1 , N , st , st + tmp.cnt[j] - 1 , j ) ;
st += tmp.cnt[j] ;
}
break;
}
default:{
for( register int j = 25 ; j >= 0 ; j -- )
if( tmp.cnt[j] ){
Modify( root , 1 , N , st , st + tmp.cnt[j] - 1 , j ) ;
st += tmp.cnt[j] ;
}
}
}
}
print( root , 1 , N ) ;
}
int main(){
freopen( "string.in" , "r" , stdin ) ;
freopen( "string.out", "w" , stdout) ;
N = read_() ; M = read_() ;
scanf( "%s" , ss + 1 ) ;
root = build( 1 , N ) ;
solve() ;
}
T2
这题的模型还是第一次见到,这题很明显是个DP。
关键在于这个DP的状态和转移,定义DP[i][j]为当前已经枚举到第i列,有j个右线段的左端点在i的左边,并且还未处理。注意,凡是我们i所到之处,右端点在i左边的左线段都必须被处理完。每次由i-1转移到i时,将右端点在该列的左线段全部处理了(相当于我们是需要把这些线段全部填充一个1,那么我们可以选择在之前的”空列&&当前列”选择一些列来使用,并且由于它们所在的行不一样,因此是排列而不是组合),并且判断左端点在i位置的右线段是否填充(如果我们把当前列用于右线段填充,那么对于左线段来说,他的可选空列就不包含当前列,但是同时由于左端点在当前位置的右线段可能有很多个,但是一列只能填充一个线段,因此还需要在排列数基础上*左端点在i位置的右线段数量),然后使用排列数和乘法原理转移。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
const int mmod = 998244353 ;
int N , M , cL[3005] , cR[3005] , sL[3005] , sR[3005] , s[3005] ;
int C[3001][3001] , fac[3001] = {1} , dp[3001][3001] ;
int main(){
// freopen( "matrix.in" , "r" , stdin ) ;
// freopen( "matrix.out", "w" , stdout) ;
scanf( "%d%d" , &N , &M ) ;
for( int i = 0 ; i <= 3000 ; i ++ ){
C[i][0] = 1 ;
for( int j = 1 ; j <= i ; j ++ )
C[i][j] = ( C[i-1][j] + C[i-1][j-1] ) %mmod ;
}
for( int i = 1 ; i <= 3000 ; i ++ )
fac[i] = 1LL * fac[i-1] * i %mmod ;
for( int i = 1 , Li , Ri ; i <= N ; i ++ ){
scanf( "%d%d" , &Li , &Ri ) ;
++ cL[Li] ; ++ cR[Ri] ;
}
for( int i = 1 ; i <= M ; i ++ ){
sL[i] = sL[i-1] + cL[i] ;
sR[i] = sR[i-1] + cR[i] ;
s[i] = sL[i] + sR[i] ;
}
dp[0][0] = 1 ;
for( int i = 1 ; i <= M ; i ++ ){
for( int j = 0 ; j <= sR[i-1] ; j ++ ){
if( !dp[i-1][j] ) continue ;
int k = i - 1 - s[i-1] + j ;
if( k < 0 ) continue ;
dp[i][ j+cR[i] ] = ( dp[i][ j+cR[i] ] + 1LL * dp[i-1][j] * C[k+1][ cL[i] ] %mmod * fac[ cL[i] ] %mmod ) %mmod ;
if( j + cR[i] )
dp[i][ j+cR[i]-1 ] = ( dp[i][ j+cR[i]-1 ] + 1LL * dp[i-1][j] * C[k][ cL[i] ] %mmod * fac[ cL[i] ] %mmod * ( cR[i] + j ) ) %mmod ;
``
}
printf( "%d" , dp[M][0] ) ;
}