Link
Aeon 显然字典序最大就是把最小的字母放在最后
Business [动态规划]
简单dp
d p [ i ] [ j ] dp[i][j] d p [ i ] [ j ] 表示到第i i i 天,当前有j j j 块钱,最后返还的钱最多为多少
完全背包转移
Celebration Description 有一个环
,求把它分成三段,使得每一段内无重复元素,且三段长度可以作为某个三角形的三边的方案数。
一个拆分方案可以看作一个三元组 ( a , b , c ) (a,b,c) ( a , b , c ) ,其中 0 < a < b < c ≤ n 0lt alt b lt c le n 0 < a < b < c ≤ n ,表示在第 a , b , c a,b,c a , b , c 个位置之前断开。两个拆分不同当且仅当其对应的三元组不同。
n ≤ 2 × 1 0 6 nle 2times10^6 n ≤ 2 × 1 0 6
Solution [计数] [树状数组]
定义长度不超过 n − 1 2 frac{n-1}{2} 2 n − 1 ,且不含重复颜色的段为合法的段。记 p r e x pre_x p r e x 为以
为右端点的合法段最远的左端点,n x t x nxt_x n x t x 为以 x x x 为左端点的合法段最远的右端点。
先枚举题目中的a a a ,那么b ∈ ( a , n x t a + 1 ] bin(a, nxt_a + 1] b ∈ ( a , n x t a + 1 ] 。在确定了a , b a, b a , b 的位置后,合法的c c c 位于( b , n x t b + 1 ] (b, nxt_b + 1] ( b , n x t b + 1 ] 和[ p r e a , n ] [pre_a, n] [ p r e a , n ] 的交集中
注意,这里的p r e a pre_a p r e a 必须是大于a a a 的,即绕了一圈绕到右边去。否则一定不合法
可以用树状数组维护:
从左往右枚举a a a ,把合法的b b b 对应的( b , n x t b + 1 ] (b, nxt_b + 1] ( b , n x t b + 1 ] 这段区间在树状数组中+1;查询就直接查[ p r e a , n ] [pre_a, n] [ p r e a , n ] 的区间和;在离开a a a 的时候把( a , n x t a + 1 ] (a, nxt_a + 1] ( a , n x t a + 1 ] 区间-1
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 #define x first #define y second #define y1 Y1 #define y2 Y2 #define mp make_pair #define pb push_back #define DEBUG(x) cout << #x << " = " << x << endl; using namespace std ;typedef long long LL;typedef pair <int , int > pii;template <typename T> inline int (T &a, T b) { return a < b ? a = b, 1 : 0 ; }template <typename T> inline int Chkmin (T &a, T b) { return a > b ? a = b, 1 : 0 ; }template <typename T> inline T read () { T sum = 0 , fl = 1 ; char ch = getchar(); for (; !isdigit (ch); ch = getchar()) if (ch == '-' ) fl = -1 ; for (; isdigit (ch); ch = getchar()) sum = (sum << 3 ) + (sum << 1 ) + ch - '0' ; return sum * fl; } inline void proc_status () { ifstream t ("/proc/self/status" ) ; cerr << string (istreambuf_iterator <char > (t), istreambuf_iterator <char > ()) << endl ; } const int Maxn = 2e6 + 10 ;int N;int A[Maxn];int vis[Maxn];int L[Maxn], R[Maxn];inline int fix (int x) { return ((x - 1 ) % N + N) % N + 1 ; }namespace BIT{ struct bit { LL sum[Maxn]; inline void add (int x, int val) { for (; x <= N; x += x & (-x)) sum[x] += val; } inline LL query (int x) { LL ans = 0 ; for (; x; x -= x & (-x)) ans += sum[x]; return ans; } } A, B; inline void update (int x, int y, int val) { if (x > y) return ; A.add (x, val * x), A.add (y + 1 , -val * (y + 1 )); B.add (x, val), B.add (y + 1 , -val); } inline LL query (int x) { return B.query (x) * (x + 1 ) - A.query (x); } inline LL query (int x, int y) { if (x > y) return 0 ; return query (y) - query (x - 1 ); } } inline void Init () { int r = 0 ; for (int i = 1 ; i <= N; ++i) { while (r < N && !vis[A[r + 1 ]]) ++r, ++vis[A[r]]; R[i] = min (r, i + (N - 1 ) / 2 - 1 ); if (vis[A[i]]) --vis[A[i]]; } memset (vis, 0 , sizeof vis); int l = 1 ; while (!vis[A[fix (l - 1 )]]) l = fix (l - 1 ), ++vis[A[l]]; L[1 ] = fix (max (l, N - (N - 1 ) / 2 + 1 )); for (int i = 2 ; i <= (N - 1 ) / 2 ; ++i) { while (vis[A[i - 1 ]]) --vis[A[l]], l = fix (l + 1 ); if (l < i) break ; L[i] = max (l, fix ((i - 1 + N) - (N - 1 ) / 2 + 1 )); ++vis[A[i - 1 ]]; } } inline void Solve () { Init (); LL ans = 0 ; int p = 1 ; for (int i = 1 ; i <= N; ++i) { if (L[i] < i) break ; while (p < N && p + 1 <= R[i] + 1 ) { ++p; BIT :: update (p + 1 , R[p] + 1 , 1 ); } ans += BIT :: query (L[i], N); BIT :: update ((i + 1 ) + 1 , R[i + 1 ] + 1 , -1 ); } cout << ans << endl ; } inline void Input () { N = read<int >(); for (int i = 1 ; i <= N; ++i) A[i] = read<int >(); } int main () {#ifdef hk_cnyali freopen("C.in" , "r" , stdin ); freopen("C.out" , "w" , stdout ); #endif Input (); Solve (); return 0 ; }
Disaster [kruskal重构树]
kruskal重构树模板题
Effort Description 有 m m m 种数据结构(可把数据结构想像成游戏中的种族),第 i i i 种有 a i a_i a i 个,每个可给敌人造成至少 1 1 1 次,至多 b i b_i b i 次伤害。有n n n 名敌人,每人承担至少一次伤害 。求总情况数模 9 9 8 2 4 4 3 5 3 998244353 9 9 8 2 4 4 3 5 3 的值
数据结构两两不同 (同种的任两个也不同),敌人两两不同。
两种方案不同当且仅当某个数据结构造成的伤害不同,或某个敌人受到的伤害不同。
n × m ≤ 1 0 5 , a i ≤ 1 0 5 , b i < 9 9 8 2 4 4 3 5 3 n times mle 10^5, a_ile 10^5, b_i < 998244353 n × m ≤ 1 0 5 , a i ≤ 1 0 5 , b i < 9 9 8 2 4 4 3 5 3
Solution [组合数学] [生成函数] [多项式] [NTT]
注意到每个敌人的伤害是无序 的,即这个敌人在被第几次攻击到都是一样的,而其他限制都是有序 的
于是可以钦定攻击顺序和受到伤害的顺序。形象地理解就是先把所有数据结构按顺序摆一排,确定每个 数据结构攻击多少次,这样就确定出一个攻击序列。再在这个攻击序列上插n − 1 n-1 n − 1 个板就是这个攻击序列方案数
看上去这是由两个部分构成的(先确定攻击序列,再插板),但实际上可以同时算
设F i ( x ) F_i(x) F i ( x ) 表示一个 第i i i 种数据结构插板方案的生成函数,它的k k k 次项系数表示插k k k 个板的方案数。那么[ x k ] F i a i ( x ) displaystyle [x^k]F_i^{a_i}(x) [ x k ] F i a i ( x ) 就是在第i i i 种里插k k k 个板的方案数了
考虑如何求F i ( x ) F_i(x) F i ( x )
第k k k 项系数其实就是
( 1 k ) + ( 2 k ) + ⋯ + ( b i k )
binom{1}{k} + binom{2}{k} + cdots + binom{b_i}{k}
( k 1 ) + ( k 2 ) + ⋯ + ( k b i )
枚举这个数据结构攻击t t t 次
那么就相当于有t t t 个空位(最后一个位置也是空位,但最后一个数据结构不是,需要单独考虑),插k k k 个板,就是( t k ) binom{t}{k} ( k t )
发现除了k = 0 k=0 k = 0 之外,都是是杨辉三角一列的之和,就等于( b i + 1 k + 1 ) binom{b_i + 1}{k + 1} ( k + 1 b i + 1 )
因为b i b_i b i 很大,不能直接算,但是k k k 比较小,所以可以先O ( 1 ) O(1) O ( 1 ) 计算出k = 1 k=1 k = 1 时的值,然后O ( 1 ) O(1) O ( 1 ) 递推下一个k k k 的值
由于只有n − 1 n-1 n − 1 个板,所以多项式长度始终不超过n − 1 n-1 n − 1 。直接做多项式快速幂即可,再把m m m 个多项式依次合起来
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 大专栏 「CometOJ」Contest #11 ne">60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 #define x first #define y second #define y1 Y1 #define y2 Y2 #define mp make_pair #define pb push_back #define DEBUG(x) cout << #x << " = " << x << endl; using namespace std ;typedef long long LL;typedef pair <int , int > pii;template <typename T> inline int (T &a, T b) { return a < b ? a = b, 1 : 0 ; }template <typename T> inline int Chkmin (T &a, T b) { return a > b ? a = b, 1 : 0 ; }template <typename T> inline T read () { T sum = 0 , fl = 1 ; char ch = getchar(); for (; !isdigit (ch); ch = getchar()) if (ch == '-' ) fl = -1 ; for (; isdigit (ch); ch = getchar()) sum = (sum << 3 ) + (sum << 1 ) + ch - '0' ; return sum * fl; } inline void proc_status () { ifstream t ("/proc/self/status" ) ; cerr << string (istreambuf_iterator <char > (t), istreambuf_iterator <char > ()) << endl ; } const int Maxn = 5e5 + 100 ;const int Mod = 998244353 ;namespace MATH{ inline void Add (int &a, int b) { if ((a += b) >= Mod) a -= Mod; } inline int Pow (int a, int b) { int ans = 1 ; for (int i = b; i; i >>= 1 , a = (LL) a * a % Mod) if (i & 1 ) ans = (LL) ans * a % Mod; return ans; } } using namespace MATH;int N, M;int A[Maxn], B[Maxn];namespace Poly{ int rev[Maxn], n; int _Wn[2 ][Maxn]; inline void init () { n = 5e5 ; for (int mid = 1 ; mid <= n; mid <<= 1 ) { _Wn[0 ][mid] = Pow (3 , (Mod - 1 ) / (mid << 1 )); _Wn[1 ][mid] = Pow (_Wn[0 ][mid], Mod - 2 ); } } inline void dft (int *A, int fg) { for (int i = 0 ; i < n; ++i) if (rev[i] < i) swap (A[rev[i]], A[i]); for (int mid = 1 ; mid < n; mid <<= 1 ) { int Wn = _Wn[fg][mid], len = mid << 1 ; for (int i = 0 ; i < n; i += len) for (int j = i, W = 1 ; j < i + mid; ++j, W = (LL) W * Wn % Mod) { int x = A[j], y = (LL) W * A[j + mid] % Mod; A[j] = (x + y) % Mod; A[j + mid] = (x - y + Mod) % Mod; } } if (fg) for (int i = 0 , inv = Pow (n, Mod - 2 ); i < n; ++i) A[i] = (LL) A[i] * inv % Mod; } inline void mul (int *A, int *B, int *C, int N) { for (n = 1 ; n <= (N << 1 ); n <<= 1 ); for (int i = 0 ; i < n; ++i) rev[i] = (rev[i >> 1 ] >> 1 ) + ((i & 1 ) ? (n >> 1 ) : 0 ); static int F[Maxn], G[Maxn]; for (int i = 0 ; i < n; ++i) F[i] = (i <= N) ? A[i] : 0 ; for (int i = 0 ; i < n; ++i) G[i] = (i <= N) ? B[i] : 0 ; dft (F, 0 ), dft (G, 0 ); for (int i = 0 ; i < n; ++i) F[i] = (LL) F[i] * G[i] % Mod; dft (F, 1 ); for (int i = 0 ; i <= (N << 1 ); ++i) C[i] = F[i]; } } int F[Maxn], G[Maxn]; int H[Maxn];inline void Solve () { ++M; A[M] = 1 , B[M] = B[M - 1 ] - 1 ; --A[M - 1 ]; G[0 ] = 1 ; for (int i = 1 ; i <= M; ++i) { if (!A[i]) continue ; #define n (B[i] + 1) #define m (j + 1) F[0 ] = (i == M) ? n : (n - 1 ); int res = (LL) n * (n - 1 ) / 2 % Mod; for (int j = 1 ; j <= N; ++j) { F[j] = res; res = (LL) res * (n - m) % Mod * Pow (m + 1 , Mod - 2 ) % Mod; } for (int j = 0 ; j <= N; ++j) H[j] = 0 ; H[0 ] = 1 ; for (int j = A[i]; j; j >>= 1 , Poly :: mul (F, F, F, N)) if (j & 1 ) Poly :: mul (H, F, H, N); Poly :: mul (G, H, G, N); #undef n #undef m } cout << G[N] << endl ; } inline void Input () { N = read<int >() - 1 , M = read<int >(); for (int i = 1 ; i <= M; ++i) A[i] = read<int >(), B[i] = read<int >(); } int main () {#ifdef hk_cnyali freopen("E.in" , "r" , stdin ); freopen("E.out" , "w" , stdout ); #endif Poly :: init (); Input (); Solve (); return 0 ; }
Farewell Description 有一张 n n n 个点 m m m 条边的图,第 i i i 条边 u i , v i u_i,v_i u i , v i 有 1 3 frac{1}{3} 3 1 的概率从u i u_i u i 指向 v i v_i v i ,另 1 3 frac{1}{3} 3 1 的概率从 v i v_i v i 指向 u i u_i u i ,剩下 1 3 frac{1}{3} 3 1 的概率被删除。求这张图是有向无环图的概率
n ≤ 2 0 nle 20 n ≤ 2 0
Solution [FWT] [子集卷积] [动态规划] [状态压缩]
设F S F_S F S 表示S S S 是DAG的方案数,E S E_S E S 表示点集S S S 内部的边数,E S , T E_{S, T} E S , T 表示S S S 与T T T 之间的边数
DAG计数显然枚举入度为0 0 0 的点容斥
F S = ∑ T ⊆ S , T ≠ ∅ ( − 1 ) ∣ T ∣ + 1 F S − T × 2 E T , S − T
F_S = sum_{Tsubseteq S, Tne emptyset} (-1)^{|T| + 1}F_{S - T}times 2^{E_{T, S - T}}
F S = T ⊆ S , T ≠ ∅ ∑ ( − 1 ) ∣ T ∣ + 1 F S − T × 2 E T , S − T 2 E T , S − T 2^{E_{T, S - T}} 2 E T , S − T 是因为从S − T S-T S − T 到T T T 的边只能断掉或指向T T T
又因为2 E T , S − T = 2 E S − E T − E S − T 2^{E_{T, S - T}} = 2^{E_S - E_T - E_{S - T}} 2 E T , S − T = 2 E S − E T − E S − T ,所以式子可以化为
F S 2 E S = ∑ T ⊆ S , T ≠ ∅ ( − 1 ) ∣ T ∣ + 1 2 E T × F S − T 2 E S − T
frac{F_S}{2^{E_S}} = sum_{Tsubseteq S, Tne emptyset} frac{(-1)^{|T| + 1}}{2^{E_{T}}} times frac{F_{S - T}}{2^{E_{S - T}}}
2 E S F S = T ⊆ S , T ≠ ∅ ∑ 2 E T ( − 1 ) ∣ T ∣ + 1 × 2 E S − T F S − T 子集卷积 即可
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 #define x first #define y second #define y1 Y1 #define y2 Y2 #define mp make_pair #define pb push_back #define DEBUG(x) cout << #x << " = " << x << endl; using namespace std ;typedef long long LL;typedef pair <int , int > pii;template <typename T> inline int (T &a, T b) { return a < b ? a = b, 1 : 0 ; }template <typename T> inline int Chkmin (T &a, T b) { return a > b ? a = b, 1 : 0 ; }template <typename T> inline T read () { T sum = 0 , fl = 1 ; char ch = getchar(); for (; !isdigit (ch); ch = getchar()) if (ch == '-' ) fl = -1 ; for (; isdigit (ch); ch = getchar()) sum = (sum << 3 ) + (sum << 1 ) + ch - '0' ; return sum * fl; } inline void proc_status () { ifstream t ("/proc/self/status" ) ; cerr << string (istreambuf_iterator <char > (t), istreambuf_iterator <char > ()) << endl ; } const int Maxn = 20 + 5 , Maxs = (1 << 20 ) + 5 ;const int Mod = 998244353 ;namespace MATH{ inline void Add (int &a, int b) { if ((a += b) >= Mod) a -= Mod; } inline int Pow (int a, int b) { int ans = 1 ; for (int i = b; i; i >>= 1 , a = (LL) a * a % Mod) if (i & 1 ) ans = (LL) ans * a % Mod; return ans; } } using namespace MATH;int N, M, ALL;int A[Maxs];int E[Maxs];int f[Maxn][Maxs], g[Maxn][Maxs];int pw[Maxn * Maxn];inline void Init () { ALL = (1 << N) - 1 ; pw[0 ] = 1 ; for (int i = 1 ; i <= M; ++i) pw[i] = (LL) pw[i - 1 ] * (Mod + 1 ) / 2 % Mod; for (int i = 1 ; i <= ALL; ++i) { int p = i & (-i); E[i] = E[i ^ p] + __builtin_popcount (A[p] & i); int len = __builtin_popcount (i); g[len][i] = (LL) ((len & 1 ) ? 1 : (Mod - 1 )) * pw[E[i]] % Mod; } } inline void DWT (int *A, int n, int op) { for (int mid = 1 ; mid < n; mid <<= 1 ) for (int i = 0 , len = mid << 1 ; i < n; i += len) for (int j = i; j < i + mid; ++j) { int x = A[j], y = A[j + mid]; if (!op) A[j + mid] = (x + y) % Mod; else A[j + mid] = (y - x + Mod) % Mod; } } inline void Solve () { Init (); f[0 ][0 ] = 1 ; DWT (f[0 ], 1 << N, 0 ); for (int i = 0 ; i <= N; ++i) DWT (g[i], 1 << N, 0 ); for (int i = 1 ; i <= N; ++i) for (int j = 0 ; j < i; ++j) for (int S = 0 ; S <= ALL; ++S) Add (f[i][S], (LL) f[j][S] * g[i - j][S] % Mod); DWT (f[N], 1 << N, 1 ); cout << (LL) f[N][ALL] * Pow (2, M) % Mod * Pow (Pow (3, M), Mod - 2) % Mod << endl; } inline void Input () { N = read<int >(), M = read<int >(); for (int i = 1 ; i <= M; ++i) { int x = read<int >() - 1 , y = read<int >() - 1 ; A[1 << x] |= (1 << y); A[1 << y] |= (1 << x); } } int main () {#ifdef hk_cnyali freopen("F.in" , "r" , stdin ); freopen("F.out" , "w" , stdout ); #endif Input (); Solve (); return 0 ; }