考试复盘
T 1 T1 T1
s u b t a s k 1 : n ≤ 5 subtask1:n\le 5 subtask1:n≤5,暴搜点的颜色状态以及边的存在状态
对于一条连接相同颜色点的边,可要可不要,不会提供形态变化的贡献, 2 e d g e 2^{edge} 2edge
d p dp dp???
设 d p [ i ] [ j ] [ 0 / 1 ] dp[i][j][0/1] dp[i][j][0/1]表示前 i i i个点,颜色为白/黑,由前面 j j j个点转移过来?
好像转移不动。。pass
T 2 T2 T2
n ≤ 15 n\le 15 n≤15??感觉给子任务编号,是不同数据下的算法拼凑而来的呢(〃 ̄︶ ̄)人( ̄︶ ̄〃)
s u b t a s k 1 : m ≤ 20 subtask1:m\le 20 subtask1:m≤20,直接 2 m 2^m 2m暴搜
竟然空间超限,ε=(・д・`*)ハァ…,哭晕在厕所
s u b t a s k 2 : 1 − 2 subtask2:1-2 subtask2:1−2之间有唯一的一条简单路径
考虑枚举在这条路径上的 i i i点相遇,则除了这条路径以外的边的方向都无所谓
s u b t a s k 3 : n ≤ 10 → m ≤ 45 subtask3:n\le 10\rightarrow m\le 45 subtask3:n≤10→m≤45
s u b t a s k 4 : subtask4: subtask4:一般情况
考虑枚举在点 x x x相遇,暴搜 1 − x 1-x 1−x每一条路径,在此基础上搜 2 − x 2-x 2−x的每一条路径,没被打标记的边可选可不选;
但是这里会出现重复的情况,因为路径并不是唯一的
emmmmmm(▼㉨▼メ)难搞哦(・。・)
T 3 T3 T3
字符匹配非常类似最近做的 F F T FFT FFT题_(:3J∠)
可仔细想想,又不太对劲,pass
猜个小结论:长度是每段只比上一段加一,感觉就是对的
考虑 d p [ i ] [ j ] dp[i][j] dp[i][j],表示串 [ l , r ] [l,r] [l,r]作为首串,最多能接的段数
转移,要枚举位置??比较串, k m p kmp kmp?? h a s h hash hash??,好像时间复杂度有点大诶
算了吧。。。
考后
唯一想说的,对于这场比赛就只有空间超限了,(*゚Д゚)つミ匚___
人生
n
≤
2
e
5
n\le 2e5
n≤2e5
设 e n d i end_i endi表示以 i i i为结尾的路径条数
如果 e n d j end_j endj是偶数条,连向 i i i后, e n d i end_i endi也只会加上偶数,奇偶性不变
如果 e n d j end_j endj是奇数条,连向 i i i后, e n d i end_i endi会加上奇数,奇偶性变化
注意 e n d i end_i endi要加上 1 1 1,自己到自己的路径
由此得知, e n d i end_i endi奇偶性只跟前面与 i i i连边的点中 e n d j end_j endj是奇数的点的个数有关,与实际点是哪个无关
设 f [ i ] [ j ] [ k ] [ h ] f[i][j][k][h] f[i][j][k][h]:表示前 i i i个点,有 j j j个白点和 k k k个黑点的 e n d end end是奇数,且前 i i i个点 e n d end end之和的奇偶性为 h h h( 0 0 0偶 1 1 1奇)
对于一个 n n n元集合( n > 1 n>1 n>1),选出一个偶数子集大小或者是一个奇数子集大小的方案数均为 2 n − 1 2^{n-1} 2n−1
简单证明一下( ̄▽ ̄)":
① n n n为奇数
假设选了 i i i个, i i i为奇数,那么 n − i n-i n−i个没有选, n − i n-i n−i为偶数
说明每选一种奇数子集大小的方案,就会存在一种偶数子集大小的方案
各自占一半, = 2 n 2 = 2 n − 1 =\frac{2^n}{2}=2^{n-1} =22n=2n−1
② n n n为偶数
设 T ( n , 1 ) T(n,1) T(n,1)表示在 n n n个中选奇数个的方案数, T ( n , 0 ) T(n,0) T(n,0)表示在 n n n个中选偶数个的方案数
考虑把 n n n分成前后各一半
Ⅰ设 n / 2 n/2 n/2为奇数
T ( n , 1 ) = T ( n / 2 , 1 ) ∗ T ( n / 2 , 0 ) + T ( n / 2 , 0 ) ∗ T ( n / 2 , 1 ) = 2 ∗ T ( n / 2 , 0 ) ∗ T ( n / 2 , 1 ) T(n,1)=T(n/2,1)*T(n/2,0)+T(n/2,0)*T(n/2,1)=2*T(n/2,0)*T(n/2,1) T(n,1)=T(n/2,1)∗T(n/2,0)+T(n/2,0)∗T(n/2,1)=2∗T(n/2,0)∗T(n/2,1)
T ( n , 0 ) = T ( n / 2 , 1 ) ∗ T ( n / 2 , 1 ) + T ( n / 2 , 0 ) ∗ T ( n / 2 , 0 ) T(n,0)=T(n/2,1)*T(n/2,1)+T(n/2,0)*T(n/2,0) T(n,0)=T(n/2,1)∗T(n/2,1)+T(n/2,0)∗T(n/2,0)
先前证明过当 x x x为奇数时,其选择奇数和偶数的方案数相等
T ( n , 0 ) = T ( n / 2 , 1 ) ∗ T ( n / 2 , 0 ) + T ( n / 2 , 0 ) ∗ T ( n / 2 , 1 ) = 2 ∗ T ( n / 2 , 0 ) ∗ T ( n / 2 , 1 ) T(n,0)=T(n/2,1)*T(n/2,0)+T(n/2,0)*T(n/2,1)=2*T(n/2,0)*T(n/2,1) T(n,0)=T(n/2,1)∗T(n/2,0)+T(n/2,0)∗T(n/2,1)=2∗T(n/2,0)∗T(n/2,1)
⇒ T ( n , 0 ) = T ( n , 1 ) \Rightarrow T(n,0)=T(n,1) ⇒T(n,0)=T(n,1)
Ⅱ设 n / 2 n/2 n/2为偶数
递归 n / 2 n/2 n/2,不停的除以 2 2 2,总有一层是奇数,就变成了Ⅰ
证毕
大力讨论列出转移方程
①点
i
+
1
i+1
i+1为白色,且
e
n
d
end
end为偶数,总和奇偶性不变
f
[
i
+
1
]
[
j
]
[
k
]
[
h
]
=
f
[
i
]
[
j
]
[
k
]
[
h
]
∗
2
k
−
1
∗
2
i
−
k
f[i+1][j][k][h]=f[i][j][k][h]*2^{k-1}*2^{i-k}
f[i+1][j][k][h]=f[i][j][k][h]∗2k−1∗2i−k
2 k − 1 2^{k-1} 2k−1:会对 i + 1 i+1 i+1奇偶性造成影响的 k k k个黑色奇数点,从中选择奇数个的方案数, e n d i + 1 end_{i+1} endi+1有这些黑色奇数点带来的奇数贡献,再加上自己到自己本身一条路径,就是偶数了
2 i − k 2^{i-k} 2i−k:剩下的点与 i + 1 i+1 i+1可连可不连,不会造成影响
②点
i
+
1
i+1
i+1为白色,且
e
n
d
end
end为奇数,总和奇偶改变
f
[
i
+
1
]
[
j
+
1
]
[
k
]
[
h
⨁
1
]
=
f
[
i
]
[
j
]
[
k
]
[
h
]
∗
2
k
−
1
∗
2
i
−
k
f[i+1][j+1][k][h\bigoplus1]=f[i][j][k][h]*2^{k-1}*2^{i-k}
f[i+1][j+1][k][h⨁1]=f[i][j][k][h]∗2k−1∗2i−k
2 k − 1 2^{k-1} 2k−1:会对 i + 1 i+1 i+1奇偶性造成影响的 k k k个黑色奇数点,从中选择偶数个的方案数
③点
i
+
1
i+1
i+1为黑色,且
e
n
d
end
end为偶数,总和奇偶性不变
f
[
i
+
1
]
[
j
]
[
k
]
[
h
]
=
h
[
i
]
[
j
]
[
k
]
[
h
]
∗
2
j
−
1
∗
2
i
−
j
f[i+1][j][k][h]=h[i][j][k][h]*2^{j-1}*2^{i-j}
f[i+1][j][k][h]=h[i][j][k][h]∗2j−1∗2i−j
2 j − 1 2^{j-1} 2j−1:会对 i + 1 i+1 i+1奇偶性造成影响的 j j j个白色奇数点,从中选择奇数个的方案数
④点
i
+
1
i+1
i+1为黑色,且
e
n
d
end
end为奇数,总和奇偶改变
f
[
i
+
1
]
[
j
]
[
k
+
1
]
[
h
⨁
1
]
=
f
[
i
]
[
j
]
[
k
]
[
h
]
∗
2
j
−
1
∗
2
i
−
j
f[i+1][j][k+1][h\bigoplus 1]=f[i][j][k][h]*2^{j-1}*2^{i-j}
f[i+1][j][k+1][h⨁1]=f[i][j][k][h]∗2j−1∗2i−j
2 j − 1 2^{j-1} 2j−1:会对 i + 1 i+1 i+1奇偶性造成影响的 j j j个白色奇数点,从中选择偶数个的方案数
如果点 i + 1 i+1 i+1被钦定为黑色,则没有①②的转移;白色则没有③④的转移
观察到每个转移方程中后面两个
2
2
2的幂乘积与
j
,
k
j,k
j,k无关,都为
2
i
−
1
2^{i-1}
2i−1
当
j
,
k
j,k
j,k为
0
0
0时,从中选偶数个为
2
i
2^i
2i,选奇数个为
0
0
0
于是,不再需要 j , k j,k j,k的实际值了
设 f [ i ] [ 0 / 1 ] [ 0 / 1 ] [ h ] f[i][0/1][0/1][h] f[i][0/1][0/1][h],表示前 i i i个点, e n d end end和的奇偶为 h h h,是否有 e n d end end为奇数的白/黑点
#include <cstdio>
#define int long long
#define mod 998244353
#define maxn 200005
int f[maxn][2][2][2];
int mi[maxn], c[maxn];
int n;
signed main() {
scanf( "%lld", &n );
for( int i = 1;i <= n;i ++ ) scanf( "%lld", &c[i] );
mi[0] = 1;
for( int i = 1;i <= n;i ++ ) mi[i] = ( mi[i - 1] << 1 ) % mod;
f[0][0][0][0] = 1;
for( int i = 0;i < n;i ++ )
for( int j = 0;j <= 1;j ++ )
for( int k = 0;k <= 1;k ++ )
for( int h = 0;h <= 1;h ++ ) {
if( ! f[i][j][k][h] ) continue;
if( c[i + 1] != 1 ) {
f[i + 1][j][k][h] = ( f[i + 1][j][k][h] + f[i][j][k][h] * ( k ? mi[i - 1] : 0 ) % mod ) % mod;
f[i + 1][j | 1][k][h ^ 1] = ( f[i + 1][j | 1][k][h ^ 1] + f[i][j][k][h] * ( k ? mi[i - 1] : mi[i] ) % mod ) % mod;
}
if( c[i + 1] != 0 ) {
f[i + 1][j][k][h] = ( f[i + 1][j][k][h] + f[i][j][k][h] * ( j ? mi[i - 1] : 0 ) % mod ) % mod;
f[i + 1][j][k | 1][h ^ 1] = ( f[i + 1][j][k | 1][h ^ 1] + f[i][j][k][h] * ( j ? mi[i - 1] : mi[i] ) % mod ) % mod;
}
}
int ans = 0;
for( int j = 0;j <= 1;j ++ )
for( int k = 0;k <= 1;k ++ )
ans = ( ans + f[n][j][k][1] ) % mod;
printf( "%lld\n", ans );
return 0;
}
赢家
n
≤
15
,
m
≤
n
×
(
n
+
1
)
2
n\le 15,m\le \frac{n\times (n+1)}{2}
n≤15,m≤2n×(n+1)
转化为用总数 2 m 2^m 2m减去不合法的方案数
不合法方案数即为, 1 1 1能到达的点集 S S S, 2 2 2能到达的点集为 T T T, S ∩ T = ∅ S∩T=\emptyset S∩T=∅
设 f [ S ] f[S] f[S]表示能使 1 1 1到达点集为 S S S内所有点的方案数, g [ T ] g[T] g[T]为能使 2 2 2到达点集为 T T T内所有点的方案数
枚举 S , T S,T S,T,两个集合内部的边的定向方案数即为 f [ S ] × g [ T ] f[S]\times g[T] f[S]×g[T](不能有边跨越 S , T S,T S,T)
对于点集 S ∪ T S∪T S∪T的补集中的点,内部边可以随便定向 2 e d g e 2^{edge} 2edge,而对于一个顶点在集合外另一个顶点在 S , T S,T S,T集合内的边,一定是从集合外指向集合内
枚举的时间复杂度为 O ( 3 n ) O(3^n) O(3n)
接下来考虑怎么计算 f [ S ] f[S] f[S], g [ T ] g[T] g[T]与之一样
一样的思路,用总数减去不合法的数量
总数为 2 e d g e _ S 2^{edge\_S} 2edge_S,
减去不合法的方案,枚举一个 S S S的真子集 s ′ s' s′,计算只能到达子集 s ′ s' s′内的点的方案数
点集 s ’ s’ s’内的方案数为 f [ s ′ ] f[s'] f[s′],点集外的方案数为 2 e d g e _ S − e d g e _ s ’ 2^{edge\_S-edge\_s’} 2edge_S−edge_s’
对于横跨 s ’ , C S s ′ s’,C_{S}s' s’,CSs′的边,方向一定是从外面指向子集 s ′ s' s′内
所以此时减去的方案数为 f [ s ′ ] × 2 e d g e _ S − e d g e _ s ′ f[s']\times 2^{edge\_S-edge\_s'} f[s′]×2edge_S−edge_s′
枚举子集, D P DP DP时间复杂度为 O ( 3 n ) O(3^n) O(3n)
#include <cstdio>
#define mod 1000000007
#define int long long
#define maxn 20
#define maxm 1 << 15
int n, m, id;
bool edge[maxn][maxn];
int in[maxm], out[maxm], mi[maxn * maxn], f[maxm], g[maxm];
signed main() {
scanf( "%lld %lld %lld", &n, &m, &id );
mi[0] = 1;
for( int i = 1, u, v;i <= m;i ++ ) {
scanf( "%lld %lld", &u, &v );
edge[u][v] = 1;
mi[i] = ( mi[i - 1] << 1 ) % mod;
}
int cnt = 1 << n;
for( int S = 0;S < cnt;S ++ )
for( int i = 1;i <= n;i ++ )
for( int j = 1;j <= n;j ++ )
if( edge[i][j] ) {
if( ( 1 << i - 1 & S ) && ( 1 << j - 1 & S ) ) in[S] ++;//完全包含在点集S里面的边
if( ( 1 << i - 1 & S ) || ( 1 << j - 1 & S ) ) out[S] ++;//完全包含在点集S里面的边+一个顶点在点集里一个顶点在点集外
}
for( int S = 0;S < cnt;S ++ ) {
if( ! ( S & 1 ) ) continue;//如果没有包含点1 跳过
f[S] = mi[in[S]];
for( int T = ( S - 1 ) & S;T;T = ( T - 1 ) & S ) {
if( ! ( T & 1 ) ) continue;//子集也必须包含点1
f[S] = ( f[S] - f[T] * mi[in[S ^ T]] % mod + mod ) % mod;
}
}
for( int S = 0;S < cnt;S ++ ) {
if( ! ( S >> 1 & 1 ) ) continue;
g[S] = mi[in[S]];
for( int T = ( S - 1 ) & S;T;T = ( T - 1 ) & S ) {
if( ! ( T >> 1 & 1 ) ) continue;
g[S] = ( g[S] - g[T] * mi[in[S ^ T]] % mod + mod ) % mod;
}
}
int ans = mi[m];
for( int S = 0;S < cnt;S ++ ) {
if( ! ( S & 1 ) || ( S >> 1 & 1 ) ) continue;
for( int T = ( cnt - 1 ) ^ S;T;T = ( T - 1 ) & ( ( cnt - 1 ) ^ S ) ) {
if( ! ( T >> 1 & 1 ) || in[S] + in[T] < in[S | T] ) continue;//如果没有包含点2 或 S,T点集有边横跨两个集合 为不合法划分 跳过
ans = ( ans - f[S] * g[T] % mod * mi[m - out[S | T]] % mod + mod ) % mod;
}
}
printf( "%lld\n", ans );
return 0;
}
黑红兔
n
≤
5
e
5
n\le 5e5
n≤5e5
性质1:存在一种方案,最后一段长度为 1 1 1,每选出的段的长度都比上一段小 1 1 1
很显然,如果某一段比下一段长 ≥ 2 \ge 2 ≥2,可以在前面或者后面去掉若干字符,只需要保证下一段是严格子串即可
设 f [ i ] [ j ] f[i][j] f[i][j]表示以子串 [ i , j ] [i,j] [i,j]为首串,能否找出 j − i + 1 j-i+1 j−i+1段符合要求
f f f为 y e s / n o yes/no yes/no的 b o o l bool bool数组
枚举下一段与 f [ i + 1 , j ] f[i+1,j] f[i+1,j]还是 f [ i ] [ j − 1 ] f[i][j-1] f[i][j−1]进行转移
用 h a s h hash hash判断两个串是否相同
性质2:答案不超过 O ( n ) O(\sqrt{n}) O(n)
最好的情况为 1 + 2 + . . . + k 1+2+...+k 1+2+...+k,此时有 k ( k + 1 ) 2 ≤ n \frac{k(k+1)}{2}\le n 2k(k+1)≤n
性质3:假设以 i i i作为第一段开头,如果第一个串长 l e n len len时,可以选出 l e n len len段,那么串长为 l e n − 1 len-1 len−1时,也能选出 l e n − 1 len-1 len−1段
非常显然,都删去一个首/尾跟下一段无关的字符,最后一段被删去,仍能保证严格子串性质
设 f [ i ] f[i] f[i]为以 i i i开始的首串,最长能选多少段
考虑二分答案,问题转化为找 [ i + m i d , n ] [i+mid,n] [i+mid,n]内是否存在一个 j j j,满足
f [ j ] ≥ m i d − 1 f[j]\ge mid-1 f[j]≥mid−1
l c p ( i , j ) ≥ m i d − 1 lcp(i,j)\ge mid-1 lcp(i,j)≥mid−1或者 l c p ( i + 1 , j ) ≥ m i d − 1 lcp(i+1,j)\ge mid-1 lcp(i+1,j)≥mid−1(看是首/尾是新添的多余字符
如果求出原串的后缀数组,那么 l c p ( i , j ) ≥ x lcp(i,j)\ge x lcp(i,j)≥x相当于 r n k j rnk_j rnkj在某个区间内,可以二分求出这个区间
此套路做法,详情可见👉亲爱的佳媛姐姐正解做法——字符串LOJ2059
O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
性质4: f [ i ] ≤ f [ i + 1 ] + 1 f[i]\le f[i+1]+1 f[i]≤f[i+1]+1
如果 i i i开始能接大于 f [ i + 1 ] + 1 f[i+1]+1 f[i+1]+1段,那么 i + 1 i+1 i+1开始一定能接大于 f [ i + 1 ] + 1 − 1 = f [ i + 1 ] f[i+1]+1-1=f[i+1] f[i+1]+1−1=f[i+1]段,有性质3得出
所以就不用二分了,类似滑动窗口一样,先把 f [ i ] f[i] f[i]赋为 f [ i + 1 ] + 1 f[i+1]+1 f[i+1]+1,然后判断是否可行,不行就一直 − 1 -1 −1直到可行为止
类似于 S A SA SA求 h e i g h t height height数组, O ( n l o g n ) O(nlogn) O(nlogn)
预处理 s t st st表里面的 l o g log log,快了整整三秒,卡(>ρ<")了
终于过了(^U^)ノ~YO
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define maxn 500005
#define INF 0x7f7f7f7f
int n, m = 255;
char s[maxn];
int tot[maxn], rnk[maxn << 1], sa[maxn], id[maxn], x[maxn], h[maxn], lg[maxn];
int st[maxn][20];
void init() {
for( int i = 1;i <= n;i ++ ) tot[x[i] = s[i]] ++;
for( int i = 1;i <= m;i ++ ) tot[i] += tot[i - 1];
for( int i = n;i;i -- ) sa[tot[x[i]] --] = i;
for( int k = 1;k <= n;k <<= 1 ) {
int num = 0;
for( int i = n - k + 1;i <= n;i ++ ) id[++ num] = i;
for( int i = 1;i <= n;i ++ ) if( k < sa[i] ) id[++ num] = sa[i] - k;
memset( tot, 0, sizeof( tot ) );
for( int i = 1;i <= n;i ++ ) tot[x[i]] ++;
for( int i = 1;i <= m;i ++ ) tot[i] += tot[i - 1];
for( int i = n;i;i -- ) sa[tot[x[id[i]]] --] = id[i];
for( int i = 1;i <= n;i ++ ) rnk[i] = x[i];
x[sa[1]] = num = 1;
for( int i = 2;i <= n;i ++ )
x[sa[i]] = ( rnk[sa[i]] == rnk[sa[i - 1]] && rnk[sa[i] + k] == rnk[sa[i - 1] + k] ) ? num : ++ num;
if( n == num ) break;
m = num;
}
for( int i = 1;i <= n;i ++ ) rnk[sa[i]] = i;
int k = 0;
for( int i = 1;i <= n;i ++ ) {
if( rnk[i] == 1 ) continue;
if( k ) k --;
int j = sa[rnk[i] - 1];
while( i + k <= n && j + k <= n && s[i + k] == s[j + k] ) k ++;
h[rnk[i]] = k;
}
lg[0] = -1;
for( int i = 1;i <= n;i ++ ) lg[i] = lg[i >> 1] + 1;
for( int i = 1;i <= n;i ++ ) st[i][0] = h[i];
for( int j = 1;j <= 18;j ++ )
for( int i = 1;i <= n;i ++ )
if( i + ( 1 << j - 1 ) > n ) break;
else st[i][j] = min( st[i][j - 1], st[i + ( 1 << j - 1 )][j - 1] );
}
int lcp( int l, int r ) {
int i = lg[r - l + 1];
return min( st[l][i], st[r - ( 1 << i ) + 1][i] );
}
int cnt;
int rt[maxn], t[maxn * 30], lson[maxn * 30], rson[maxn * 30];
void modify( int pre, int &now, int l, int r, int pos, int val ) {
if( ! now ) now = ++ cnt;
if( l == r ) { t[now] = max( t[now], val ); return; }
int mid = ( l + r ) >> 1;
if( pos <= mid ) rson[now] = rson[pre], modify( lson[pre], lson[now], l, mid, pos, val );
else lson[now] = lson[pre], modify( rson[pre], rson[now], mid + 1, r, pos, val );
t[now] = max( t[lson[now]], t[rson[now]] );
}
int query( int now, int l, int r, int L, int R ) {
if( ! now || R < l || r < L ) return 0;
if( L <= l && r <= R ) return t[now];
int mid = ( l + r ) >> 1;
return max( query( lson[now], l, mid, L, R ), query( rson[now], mid + 1, r, L, R ) );
}
int find_l( int pos, int len ) {
int l = 1, r = pos - 1;
while( l <= r ) {
int mid = ( l + r ) >> 1;
if( lcp( mid + 1, pos ) >= len ) r = mid - 1;
else l = mid + 1;
}
return l;
}
int find_r( int pos, int len ) {
int l = pos + 1, r = n;
while( l <= r ) {
int mid = ( l + r ) >> 1;
if( lcp( pos + 1, mid ) >= len ) l = mid + 1;
else r = mid - 1;
}
return r;
}
bool check( int pos, int len ) {
if( query( rt[pos + len], 1, n, find_l( rnk[pos], len - 1 ), find_r( rnk[pos], len - 1 ) ) >= len - 1 )
return 1;
if( query( rt[pos + len], 1, n, find_l( rnk[pos + 1], len - 1 ), find_r( rnk[pos + 1], len - 1 ) ) >= len - 1 )
return 1;
return 0;
}
int ans;
int f[maxn];
int main() {
scanf( "%s", s + 1 );
n = strlen( s + 1 );
init();
ans = f[n] = 1;
modify( rt[n + 1], rt[n], 1, n, rnk[n], f[n] );
for( int i = n - 1;i;i -- ) {
f[i] = f[i + 1] + 1;
while( ! check( i, f[i] ) ) f[i] --;
ans = max( ans, f[i] );
modify( rt[i + 1], rt[i], 1, n, rnk[i], f[i] );
}
printf( "%d\n", ans );
return 0;
}