"LibreOJ NOI Round #2" unequal relationship (dp+NTT divide and conquer)

description

Poke me to see the title

solution

There is a very similar question

For a tree, each edge limits the size relationship between two endpoints (limit a [u]> a [v] a[u]>a[v]a[u]>a[v] a [ u ] < a [ v ] a[u]<a[v] a[u]<a [ v ] )
Find the number of permutations that meet the requirementsaaa Meets the limit of the whole tree. n <= 5000 n<=5000n<=5000

Consider that if all edges are in one direction, it is easy to do. The
answer is n! n!n ! Divide by the size of each subtree.
If there are reverse edges, brute force enumeration breaks several reverse edges, the remaining edges are changed to forward, and then the answer is calculated
. The complexity of this violence isO (2 n ∗ n) O(2^n*n)O ( 2nn- ) is
considereddp dpdp f ( i , j , k ) f(i,j,k) f(i,j,k ) meansiii is the subtree of the root, the currentiii hasjjin the connected blockj points, total reversekkThe number of solutions for k edges When
merging two subtrees, if the edges are positive, then merge directly;
otherwise, either disconnect or letk + 1 k+1k+1 and merge according to the forward direction.
ComplexitynnSeveral powers of n ,
considering the final tolerance, only need to pay attention tokkThe parity of k , so the third dimension can be completely omitted.
That is, when merging two subtrees, if the edge is positive, then merge directly, otherwise the value is the plan of disconnection minus the plan of forwarding the edge,
so it is a simple tree backpack. ComplexityO (n 2) O(n^2)O ( n2)

This question only needs to change the two-dimensional dp dpd p can be optimized again,
setdp [i] dp[i]d p [ i ] means prefixiii number of valid programs,CNT [i] CNT [i]c n t [ i ] means prefixiii > > >的个数
d p [ i ] i ! = ∑ j = 0 i − 1 [ s [ j ] = ′ > ′ ] ( i − j ) ! ( − 1 ) c n t [ i − 1 ] − c n t [ j ] × d p [ j ] j ! \frac{dp[i]}{i!}=\sum_{j=0}^{i-1}\frac{[s[j]='>']}{(i-j)!}(-1)^{cnt[i-1]-cnt[j]}\times \frac{dp[j]}{j!} i!dp[i]=j=0i1(ij)![s[j]=>](1)cnt[i1]cnt[j]×j!dp[j]
( − 1 ) c n t [ i − 1 ] (-1)^{cnt[i-1]} (1)c n t [ i 1 ] is proposed, and the remaining part isNTT NTTN T T divide and conqueris difficult tocomplete

Insert picture description here

code

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define mod 998244353
#define int long long 
#define maxn 400005
int len, inv;
char s[maxn];
int cnt[maxn];
int fac[maxn], ifac[maxn], r[maxn];
int f[maxn], g[maxn], dp[maxn];

int qkpow( int x, int y ) {
    
    
	int ans = 1;
	while( y ) {
    
    
		if( y & 1 ) ans = ans * x % mod;
		x = x * x % mod;
		y >>= 1;
	}
	return ans;
}

void NTT( int *c, int opt ) {
    
    
	for( int i = 0;i < len;i ++ )
		if( i < r[i] ) swap( c[i], c[r[i]] );
	for( int i = 1;i < len;i <<= 1 ) {
    
    
		int omega = qkpow( opt == 1 ? 3 : mod / 3 + 1, ( mod - 1 ) / ( i << 1 ) );
		for( int j = 0;j < len;j += ( i << 1 ) ) {
    
    
			int w = 1;
			for( int k = 0;k < i;k ++, w = w * omega % mod ) {
    
    
				int x = c[j + k], y = w * c[j + k + i] % mod;
				c[j + k] = ( x + y ) % mod;
				c[j + k + i] = ( x - y + mod ) % mod;
			}
		}
	}
	if( opt == -1 ) {
    
    
		int inv = qkpow( len, mod - 2 );
		for( int i = 0;i < len;i ++ )
			c[i] = c[i] * inv % mod;
	}
}

void solve( int L, int R ) {
    
    
	if( L == R ) {
    
    
		if( ! L ) dp[L] = 1;
		else dp[L] = cnt[L] & 1 ? mod - dp[L] : dp[L];//单独提出来 
		return;
	}
	int mid = ( L + R ) >> 1;
	solve( L, mid );
	len = 1; int l = 0;
	while( len <= R - L + 1 + mid - L ) len <<= 1, l ++;
	for( int i = 0;i < len;i ++ )
		r[i] = ( r[i >> 1] >> 1 ) | ( ( i & 1 ) << ( l - 1 ) );
	for( int i = 0;i <= mid - L;i ++ )
		if( s[i + L] == '<' && i + L != 0 ) f[i] = 0;
		else f[i] = cnt[i + L] & 1 ? dp[i + L] : mod - dp[i + L];//注意奇偶转换 
	for( int i = mid - L + 1;i < len;i ++ ) f[i] = 0;
	for( int i = 0;i <= R - L + 1;i ++ ) g[i] = ifac[i];
	for( int i = R - L + 2;i < len;i ++ ) g[i] = 0;
	NTT( f, 1 );
	NTT( g, 1 );
	for( int i = 0;i < len;i ++ ) f[i] = f[i] * g[i] % mod;
	NTT( f, -1 );
	for( int i = mid + 1;i <= R;i ++ ) dp[i] = ( dp[i] + f[i - L] ) % mod;
	solve( mid + 1, R );
}

signed main() {
    
    
	scanf( "%s", s + 1 );	
	int n = strlen( s + 1 );
	s[++ n] = '>';
	fac[0] = 1;
	for( int i = 1;i <= n;i ++ )
		fac[i] = fac[i - 1] * i % mod;
	ifac[n] = qkpow( fac[n], mod - 2 );
	for( int i = n - 1;~ i;i -- )
		ifac[i] = ifac[i + 1] * ( i + 1 ) % mod;
	for( int i = 1;i <= n;i ++ )
		cnt[i] = cnt[i - 1] + ( s[i] == '>' );
	solve( 0, n );
	printf( "%lld\n", dp[n] * fac[n] % mod );
	return 0;
}

Guess you like

Origin blog.csdn.net/Emm_Titan/article/details/113818488