目录:
一、DP
二、图论
1、最短路
2、强连通分量
三、利用单调性维护
四、贪心
五、数据结构
1、并查集
六、数学
1、计数问题
2、数学分析
七、博弈
八、搜索
//////////////////////////////////
一、DP:
1003:
(参见 http://hi.baidu.com/aekdycoin/item/88a8be0bf621c6314ac4a3d5 )
然后就是一个区间DP的问题
DP[i][j] 表示从第 i 天到第 j天的最优值,于是方程很显然:
DP[i][j] = min{DP[i][w] + K + DP[w+1][j]} (i<=w<j)
;
; i <= tot; ++i)
, ;
<< eg[i].v))
;
, m, m);
);
<= r; ++i) {
, r) + kkcld, z);
, , ;
; i < e; ++i) {
; i < d; ++i) {
scanf( ar[j] |= ( << p);
}
}
}
memset(d, -, printf(, n));
}
== scanf( input();
solve();
}
;
}
1009:
KMP预处理+矩阵快速幂加速DP
可以参考( http://hi.baidu.com/wyl8899/item/dc5abdccb571efd597445268 )
f[i, j]代表字符串匹配到第i位时已经匹配了不吉利数字1到j位 时的方案数
KMP预处理状态转移,由于转移方程式都是一样的,可以用矩阵快速幂优化
#include <cstdio>
#include <algorithm>
#include <cstring>
;
memset(val, , }
; i < r; ++i)
val[i][i] = ;
}
};
Matrix M, ans;
Matrix multi(Matrix a, Matrix b) {
Matrix re;
re.r = a.r, re.c = b.c;
re.clear();
; i < re.r; ++i)
; j < re.c; ++j)
; k < re.c; ++k)
re.val[i][j] = (re.val[i][j] + (a.val[i][k] * b.val[k][j]) % mod) % mod;
}
Matrix Pow(Matrix a, Matrix re;
re.clear();
re.r = re.c = a.r;
re.One();
) re = multi(re, a);
a = multi(a, a);
x >>= ;
}
}
f[] = f[] = ;
M.r = m, M.c = m;
M.clear();
; i < m; ++i) {
j = f[i];
] = j + ;
] = ;
}
; i < m; ++i) {
; j < ; ++j) {
p = i;
) ];
}
}
ans.r = , ans.c = m;
ans.clear();
ans.val[][] = ;
}
== scanf( ; i < m; ++i)
scanf( prepare();
tot = ;
M = Pow(M, n);
ans = multi(ans, M);
; i < m; ++i)
tot = (tot + ans.val[][i]) % mod;
printf(
}
;
}
首先是一个很明显的O(N^2)的dp
dp[i] = dp[j] + min(sum[i] - sum[j] + i - j - 1 + L)^2
dp[i] = min(dp[j] + (sum[i] - sum[j] + i - j - 1 + L)^2)
可以用线段树或者单调队列优化到O(NlogN) ,参见
jsoi2009论文《用单调性优化动规》( http://wenku.baidu.com/view/83e4fec59ec3d5bbfd0a74e1.html )
《1D\1D动态规划初步》 ( http://wenku.baidu.com/view/681d161ca300a6c30c229f70.html )
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
;
node() {
}
node( l = _l, r = _r, id = _id;
}
};
deque<node> Q;
typ d[N], sum[N], L, c[N];
; i <= n; ++i)
scanf( sum[] = ;
; i <= n; ++i)
sum[i] = sum[i - ] + c[i];
}
typ sqr(typ x) {
}
typ Cal( ] + r - l - L);
}
node u;
Q.pop_back();
Q.push_front(node(, n, ));
d[] = ;
; i <= n; ++i) {
u = Q.front();
d[i] = d[u.id] + Cal(u.id + , i);
Q.push_back(node(i + , n, i));
}
u = Q.back();
, u.l) >= d[i] + Cal(i + , u.l)) {
Q.pop_back();
}
, u.r) <= d[i] + Cal(i + , u.r)) {
Q.push_back(node(u.r + , n, i));
}
mid = (L + R) >> ;
, mid) < d[i] + Cal(i + , mid)) {
L = mid + ;
} R = mid;
}
}
Q.back().r = L - ;
Q.push_back(node(L, n, i));
}
}
printf( }
== scanf( input();
solve();
}
;
}
也可以用经典的斜率优化 复杂度是O(N),参见
《从一类单调性问题看算法的优化》 ( http://wenku.baidu.com/view/fa5e7243b307e87101f69683.html )
《数形结合的应用——浅谈动态规划中的斜率优化》
( http://wenku.baidu.com/view/66304ff4ba0d4a7302763ae7.html )
deque<
sum[] = ;
; i <= n; ++i) {
scanf( sum[i] += sum[i - ];
}
}
LL A( }
LL B( }
LL sqr(LL x) {
}
LL Up( }
LL Down( }
}
LL Cal( + sum[j] - sum[i] - L);
}
Q.pop_back();
d[] = ;
Q.push_back();
; i <= n; ++i) {
) {
u = Q.front();
Q.pop_front();
v = Q.front();
Q.push_front(u); }
}
u = Q.front();
d[i] = Cal(u, i);
) {
v = Q.back();
Q.pop_back();
u = Q.back();
Q.push_back(v); }
}
Q.push_back(i);
}
printf( }
== scanf( input();
solve();
}
;
}
1021:
DP: d[i][j][k] //只允许交换前0到i-1种前,使得第一个人拥有j元,第二个人拥有k元 所花费的最少交换次数
然后枚举第i种货币的分配方案,两层循环分别枚举第一个人,第二个人交换完第i种货币后手里还有几枚。
有的题解说要用最大公约数剪枝,我没有试过,总之速度还算行。
#include <algorithm>
#include <cstring>
;
;
][N];
], mon[N];
+ ;
][MAX][MAX];
, , , , , };
== x)
x = y;
x = std::min(x, y);
}
memset(mon, ,
;
; i < ; ++i) {
sum[i] = ;
; j >= ; --j) {
scanf( mon[j] += own[i][j];
sum[i] += own[i][j] * val[j];
}
tot += sum[i];
}
memset(d[], -, ]));
d[][sum[]][sum[]] = ;
; i < N; ++i) {
;
memset(d[ - now], -, ]));
; j <= tot; ++j) {
; k + j <= tot; ++k) {
) {
update(d[ - now][j][k], d[now][j][k]);
; a <= mon[i]; ++a) {
; b + a <= mon[i]; ++b) {
][i]);
][i]);
&& sumb >= && suma + sumb <= tot) {
][i]) + std::abs(b - own[][i]) + std::abs(mon[i] - a - b - own[][i])) / ;
update(d[ - now][suma][sumb], d[now][j][k] + dis);
}
}
}
}
}
}
}
], eb = sum[], ec = sum[];
ea -= x1; eb += x1;
eb -= x2; ec += x2;
ec -= x3; ea += x3;
|| eb < || ec < || ea + eb + ec != tot || d[N & ][ea][eb] < )
puts( printf(][ea][eb]);
}
== scanf( work();
}
;
}
1025:
(参见 http://www.google.com.hk/url?sa=t&rct=j&q=[SCOI2009]%E6%B8%B8%E6%88%8F&source=web&cd=9&ved=0CFsQFjAI&url=http%3a%2f%2fabyss.ylen.me%2farchives%2f55&ei=Y3QsUsC-Aa6viQfsmoDYDA&usg=AFQjCNEyp1WqBW9xrWIA8i277vs_PMkKGw )
通过分析,我们发现,如果一个数的循环节是x,那么一定有x个数的循环节是x。因为一个数在他的循环中,不可能两次遇到同一个数。
而排列数就是每个循环节的LCM。
于是我们将问题抽象成:将N分割成k个数(k <= N),即(a1+....+ak) = N,问我们LCM(a1, a2, ...., ak)有多少种?
由于1对LCM是没有影响的,所以问题又变成了(a1+a2+....+ak) <= N,那么LCM(a1, a2, ..., ak)有多少种?
我们可以DP解决
d[i][j]//前0到i-1个素数组成和为j时的方案数(j = (pime[0]^C0 + prime[1]*C1 + .... + prime[i - 1]*Ci - 1)
= d[i - 1][j] + sigme{d[i - 1][j - k]};//k = prime[i]^c;(c >= 1)
#include <cstring>
#include <algorithm>
#include <vector>
typedef
+ ;
ll d[N][N];
std::vector<
prime.clear();
memset(vis, , ; i <= n; ++i) {
prime.push_back(i);
vis[j] = }
}
}
== scanf( prepare();
memset(d, , d[][] = ;
; i < prime.size(); ++i) {
; j <= n; ++j) {
) d[i + ][j] += d[i][j];
d[i + ][j + z] += d[i][j];
}
}
}
ll ans = ;
; i <= n; ++i)
ans += d[prime.size()][i];
printf( }
;
}
1026:
数位dp:
d[i][j]//以i做为开头,长度为j的数,可以组成多少个windy数 (注意,当i为0的时候,i不能是前导0)
#include <algorithm>
#include <cstring>
#include <vector>
+ ;
== len) {
;
}
d[x][len] = ;
; i < ; ++i)
d[x][len] += dp(i, len - );
; i >= ; --i) {
d[x][len] += dp(i, len - );
}
}
std::vector<
) {
;
; i <= ar[len - ]; ++i)
++re, ++z;
, ar[len - ]); i >= ; --i)
++re, ++z;
} , ar[len - ]); i >= ; --i) {
]) {
dfs(re, len - );
} re += dp(i, len);
}
}
; i <= ar[len - ]; ++i) {
]) {
dfs(re, len - );
} re += dp(i, len);
}
}
}
}
) ;
== x) ;
ar.clear();
;
ar.push_back(x % );
x /= ;
}
== ar.size())
] + ;
; i < ; ++i)
; j < len; ++j)
re += dp(i, j);
++re;
; i <= ar[len - ]; ++i) {
]) {
dfs(re, len - );
} re += dp(i, len);
}
}
}
);
printf( }
memset(d, -,
== scanf( work();
}
;
}
1037:
DP:d[i][j][k]/以i-1个数结尾的所有连续断都满足条件,以第i个数结尾的所有连续段中,男生最多比女生多j个,女生最多比男生多k个
d[i][j][k] = d[i - 1][j + 1][k - 1] //第i个是男生
+ d[i - 1][j - 1][k + 1];//第i个是女生
由于内存问题,需要用滚动数组
#include <algorithm>
#include <cstring>
;
+ ;
+ ;
][M][N][N];
memset(d, , d[][][][] = ;
; i <= (n + m); ++i) {
, pre = - now;
memset(d[now], , ]));
; j <= std::min(i, n); ++j)
; k1 <= std::min(i, lim); ++k1) {
; k2 <= std::min(lim, i - j); ++k2) {
][k1 + ][std::max(, k2 - )];
z = (z + d[pre][j][k1][k2]) % mod;
, k1 - )][k2 + ];
zz = (zz + d[pre][j][k1][k2]) % mod;
}
}
}
;
; k1 <= lim; ++k1)
; k2 <= lim; ++k2)
sum = (sum + d[(n + m) & ][n][k1][k2]) % mod;
printf( }
== scanf( work();
}
;
}
1044:
第一问是经典的二分题,二分最大长度的最小值,贪心判断
第二问dp做:
d[i,j] //前j个木棍,恰好砍了i刀的方案数
= d[i - 1, k]; //k是所有满足 sum(k + 1, j) <= 第一问的结果的值
我们发现,此处具有决策的单调性,所以我们模拟队列解决
另外由于空间问题需要滚动数组
#include <algorithm>
#include <cstring>
;
+ ;
][N];
, tot = ;
; i <= n; ++i) {
tot = tot + ar[i];
++re;
tot = ar[i];
}
}
}
sum[] = ar[] = ;
; i <= n; ++i) {
scanf( sum[i] = sum[i - ] + ar[i];
}
l = ; r = sum[n];
) {
mid = (l + r) / ;
r = mid;
l = mid;
}
printf(
memset(d[], , ]));
; i <= n; ++i)
++d[][i];
, a = d[][n];
; j <= m; ++j) {
, pre = - now;
memset(d[now], ,
presum = tot = ;
idx = ;
; i <= n; ++i) {
tot = tot + ar[i];
presum = (presum + d[pre][i - ]) % mod;
tot = tot - ar[idx + ];
presum = (presum - d[pre][idx] + mod) % mod;
++idx;
}
d[now][i] = presum;
}
a = (a + d[now][n]) % mod;
}
printf( }
== scanf( work();
}
;
}
1046:
首先倒着做O(NlogN)的LIS,然后深搜
#include <algorithm>
#include <cstring>
#include <vector>
;
+ ;
l = ,r = n;
) {
mid = (l + r) >> ;
r = mid;
l = mid;
}
g[r] = val;
}
std::vector<
; i <= n; ++i)
scanf( std::fill(g, g + n + , -INF);
;
; i <= n; ++i) {
d[n - i + ] = bsearch(ar[n - i + ]);
mx = std::max(mx, d[n + - i]);
}
scanf( scanf( puts( ans.clear();
ans.push_back(-INF);
; i <= n; ++i) {
]) {
ans.push_back(ar[i]);
) }
}
; i < ans.size(); ++i) {
)
putchar( printf( }
putchar( }
}
}
== scanf( work();
}
;
}
1048:
陈题了,黑书上DP那章有介绍, 要求均方差最小,可以先将公式变形,发现只要各部分平方和最大即可
#include <algorithm>
#include <cstring>
#include <cmath>
;
+ ;
;
re += ar[i][j];
}
== ti) {
}
z = INF;
; j < ti; ++j)
z = std::min(z, dp(x1, y1, i, y2, j) + dp(i + , y1, x2, y2, ti - j));
; j < ti; ++j)
z = std::min(z, dp(x1, y1, x2, i, j) + dp(x1, i + , x2, y2, ti - j));
}
}
;
; i <= r; ++i)
; j <= c; ++j) {
scanf( tot += ar[i][j];
}
memset(d, -,
, , r, c, lim);
. * avg * tot / lim + avg * avg);
printf( }
== scanf( work();
}
;
}
1049:
我们要修改最少的数让原序列变成一个严格单调上升序列(不重复)。
我们可以先读入每一个数,然后ar[i] - i后,问题转化成为修改最少的数让原序列变成非严格单调上升序列(可重复),
由此我们可以知道,最终序列中的每一个数在原序列都出现过。我们可以将原序列的数提取出来,离散化,然后做dp。
f[i,j]//前i个数已经非严格单调增,且第i位的值<=j,
f[i,j] = min(f[i, j - 1], f[i - 1, j - 1] + cost); //枚举第i位的值
#include <algorithm>
};
+ ;
node d[N];
inline ) {
a.val = nval;
a.cos = ncos;
} a.cos = std::min(a.cos, ncos);
}
} a.val = nval;
a.cos = ncos;
}
}
; i <= n; ++i) {
scanf( ar[i] = ar[i] - i;
x[i - ] = ar[i];
}
std::sort(x, x + n);
; i < idx; ++i)
d[i].cos = d[i].val = ;
; i <= n; ++i) {
; j < idx; ++j) {
) {
d[j].val = d[j].val + ;
d[j].cos = d[j].cos + std::abs(ar[i] - x[j]);
}
&& d[j - ].val >= ) {
update(d[j], d[j - ].val, d[j - ].cos);
}
}
node ans;
ans.val = -; ans.cos = ;
; i < idx; ++i)
)
update(ans, d[i].val, d[i].cos);
printf( }
== scanf( work();
}
;
}
1072:
c++用STL中next_permutation枚举,set判重,可以直接过,但是速度不够快。
#include <algorithm>
#include < #include <cstring>
typedef + ;
std::
v.clear();
scanf(
;
; s[i]; ++i) {
ar[idx ++] = s[i] - }
ll ans = ;
std::sort(ar, ar + idx);
ll num = ;
; i < idx; ++i) {
num = num * + ar[i];
}
) {
++ans;
v.insert(num);
}
}
printf( }
scanf( ) {
work();
}
;
}
比较好的方法是状压DP,
d[s][i] //集合s表示已经被取走了的数的集合,i表示这些数可以有多少种组成方式使得组成的数mod d = i
我们有转移方程
d[s | (1 << ar[k])][(i * 10 + ar[k]) % d] += d[s][i]; //s中没有第k个数
#include <algorithm>
#include <cstring>
typedef + ;
ll dp[ << ][N];
scanf(
;
; ch[i]; ++i) {
ar[idx ++] = ch[i] - }
ll ans = , tmp = ;
std::sort(ar, ar + idx);
; i < idx;) {
;
i = j; }
== idx) i = idx;
}
; j <= cnt; ++j) {
tmp = tmp * j;
}
}
memset(dp, , dp[][] = ;
<< idx;
; i < idx; ++i) {
) {
; k < idx; ++k)
dp[ << k][ar[k] % d] = ;
} << i) - ;
; j < d; ++j) {
)
; k < idx; ++k) {
)) {
dp[s | ( << k)][(j * + ar[k]) % d] += dp[s][j];
}
}
}
s = ((s & ~y) / x >> ) | y;
}
}
}
printf(][] / tmp);
}
scanf( ) {
work();
}
;
}
1084:
我们注意到列最大只有2,所以分开讨论,当列只有1的时候
d[i, j]//已经取完前i行,累计取了j个矩阵
-> d[k,j] //第i + 1行到第k行不取
-> d[k,j + 1]//取第i+1到第k行的矩阵
列为2的时候差不多,d[i,j,k]//第一列取到了第i行,第2列取到了第j行,累计取了k个矩阵
+ ;
+ ;
+ ;
;
; i <= r; ++i)
; j <= c; ++j)
== c) {
; i <= r; ++i)
; j <= lim; ++j)
][] = ;
; i < r; ++i) {
; j <= lim; ++j) {
; k <= r; ++k) {
], rd[i][j] + sum(i + , , k, ));
== lim) update(ans, rd[k][j + ]);
; i <= r; ++i)
; j <= r; ++j)
; k <= lim; ++k)
][][] = ;
; i <= r; ++i)
; j <= r; ++j) {
; k <= lim; ++k) {
, j + );
], d[i][j][k] + sum(z, , to, ));
== lim) update(ans, d[to][to][k + ]);
; to <= r; ++to) {
], d[i][j][k] + sum(i + , , to, ));
== lim) update(ans, d[to][j][k + ]);
; to <= r; ++to) {
], d[i][j][k] + sum(j + , , to, ));
== lim) update(ans, d[i][to][k + ]);
== lim) ans = ;
== scanf( }
;
}
/////////////////////////////////////
二、图论
1、最短路
1001:
点太多,网络流会T掉。利用平面图性质转换成偶图,然后将网络流转换成最短路,参见周冬论文 《两极相通——浅谈最大最小定理在信息学竞赛中的应用》
(链接君 http://wenku.baidu.com/view/f8239d1910a6f524ccbf85ce.html )
;
; i <= tot; ++i)
, ;
;
;
;
, ;
; ed = (n - ) * * m + (m - ) * + ;
; i <= n; ++i) {
; j <= m - ; ++j) {
* (i - ) + j * ;
* (i - ) + j * - ;
== i) {
; i <= n - ; ++i)
; j <= m; ++j) {
* (i - ) + j * - ;
* (i - ) + (j - ) * ;
== j) {
; i <= n - ; ++i)
; j <= m - ; ++j) {
id = m * * (i - ) + j * ;
nid = id - ;
addedge(id, nid, len);
addedge(nid, id, len);
}
}
printf( }
== scanf( == n && == m) puts( == n) {
mx = INF;
; i <= m - ; ++i) {
scanf( mx = min(u, mx);
}
printf( } == m) {
mx = INF;
; i <= n - ; ++i) {
scanf( mx = min(mx, u);
}
printf( }
input();
solve();
}
;
}
羞涩的代码君
1050:
枚举最小的边,对最短路变形暴力跑一遍。if (max(d[i], w[i, j]) < d[j]) d[j] = max(d[i], w[i, j]);
+ ;
+ ;
+ ];
, ; i <= ; ++i) {
; j += i)
, INF);
, ;
);
; i < prime.size(); ++i) {
== re.first % prime[i] && == re.second % prime[i]) {
, ;
;
; i < m; ++i) {
scanf( addedge(u, v, len);
addedge(v, u, len);
x[idy ++] = len;
}
std::sort(x, x + idy);
idy = std::unique(x, x + idy) - x;
scanf(
pii ans(INF, );
; i < idy; ++i)
ans = Min(ans, Cal(x[i], s, t));
puts( } )
printf( printf( }
}
prepare();
== scanf( work();
}
;
}
2、强连通分量
1051:
先求强连通块,然后重构图,统计出度为0的块的个数,如果为1,输出那一块的大小,如果大于一输出零
;
) {
];
, , ;
; i < n; ++i) {
, ;
; i < m; ++i) {
== sol.scc_cnt) {
, ; i < n; ++i)
, idx;
; i <= sol.scc_cnt; ++i) {
) {
cnt = ;
; i < n; ++i)
printf( } }
}
== scanf( input();
solve();
}
;
}
///////////////////////////////////////
三、利用单调性维护
1012:
可以用单调栈来做,维护一个单调增的栈(如果Ai > Aj 并且 i > j, 那么Ai 总是比 Aj 更加有可能成为答案),然后每次二分查找答案。
当然,更加暴力的方法是直接线段树搞。
#include <cstdio>
#include <algorithm>
#include <cstring>
;
];
, r = c - , mid;
mid = (l + r) >> ;
) {
l = mid + ;
} r = mid;
}
}
}
== scanf( c = cc = t = ;
; i <= m; ++i) {
scanf( u = (u + t) % d;
ar[++cc] = u;
&& ar[sta[c - ]] < u) --c;
sta[c ++] = cc;
} t = bSearch(u, cc);
printf( }
}
}
;
}
1047:
要在一个1000*1000的矩形中找到一个正方形,使得这个正方形中的最大值-最小值最小。
首先我们可以用单调队列维护
rmax[a][b]//从(a,b)这个点向右连续n个点的最大值
rmin[a][b]//从(a,b)这个点向右连续n个点的最小值
复杂度是O(A*B)
然后
qmax[a][b]//以(a,b)为左上角的矩形中的最大值
就等于max(rmax[a][b], .....rmax[a + x - 1][b]);
那么我们就将二维问题变成了一维问题,这个也用单调队列扫一遍就好了
== z) {
== z) {
;
, , ; i <= r; ++i) {
; j < s; ++j) {
], Max.front().v);
], Min.front().v);
, , ; j + s - <= c; ++j) {
; i < s; ++i) {
Min.pop_back();
}
Min.push_back(node(i, j, rmin[i][j]));
}
Max.pop_back();
}
Max.push_back(node(i, j, rmax[i][j]));
Min.pop_back();
}
Min.push_back(node(i, j, rmin[i][j]));
Max.pop_front();
Min.pop_front();
update_max(qmax[i - s + ][j], Max.front().v);
update_min(qmin[i - s + ][j], Min.front().v);
}
}
}
; i <= r; ++i)
; j <= c; ++j)
scanf(
prepare();
; i + s - <= r; ++i) {
; j + s - <= c; ++j) {
}
}
printf( }
== scanf( work();
}
;
}
/////////////////////////////////////////
四、贪心
1029:
参见论文《浅谈信息学竞赛中的区间问题》(链接
首先我们将所有事件按照结束时间从小到大排序。//理由很显然吧
然后就是一个分阶段的问题,假设当前已经处理完了前i - 1个事件,取了k个事件,总时间是d[i],如果此时还可以选取事件i,那么最优方案显然是选取事件i。
如果不能选取事件i,那么我们已经无法增大k了,但是我们还有可能减小d[i],如果第i个事件解决所需要的时间小于已经取了的k个事件的最大值,那么可以用第i个事件替换那个事件。
用一个优先队列维护就好了。
#include <queue>
#include <algorithm>
typedef std::pair< + ;
pii ar[N];
std::priority_queue<pii>Q;
}
; i < n; ++i)
scanf(
std::sort(ar, ar + n, cmp);
;
; i < n; ++i) {
Q.push(ar[i]);
now += ar[i].first;
} now = now + ar[i].first - Q.top().first;
Q.pop();
Q.push(ar[i]);
}
}
printf( }
== scanf( work();
}
;
}
1034:
经典的田忌赛马。
参加( http://oibh.info/archives/261 )
#include <algorithm>
+ ;
;
al = bl = ;
ar = br = n - ;
--ar; --br;
re += ;
} ++al; ++bl;
re += ;
} ++al; --br;
re += ;
} ++al; --br;
}
}
}
; i < n; ++i)
scanf( ; j < n; ++j)
scanf(
std::sort(a, a + n);
std::sort(b, b + n);
printf( * n - solve(b, a));
}
== scanf( work();
}
;
}
///////////////////////////////////
五、数据结构
1、并查集
1015:
倒着做并查集
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
;
;
};
Edge eg[N];
}
--cnt; fa[xx] = yy;
}
eg[idx].u = u, eg[idx].v = v, eg[idx].nex = g[u];
g[u] = idx++;
}
; i < n; ++i)
fa[i] = i;
cnt = n; idx = ;
memset(can, , memset(g, -, ; i < m; ++i) {
scanf( addedge(u, v);
}
scanf( ; i < c; ++i) {
scanf( can[ar[i]] = }
}
; i < n; ++i) {
}
}
;
ans[cc++] = cnt - c;
; i >= ; --i) {
can[ar[i]] = }
ans[cc++] = cnt - i;
}
; i >= ; --i)
printf( }
== scanf( input();
solve();
}
;
}
六、数学
1、计数问题
1005:
参见( http://hi.baidu.com/aekdycoin/item/589a99d3fad167352b35c7d4 )
BigInteger re = BigInteger.ONE;
re = re.multiply(BigInteger.valueOf(i));
}
Scanner cin = k = cin.nextInt();
sum += k - 1;
ar[c++] = k - 1;
}
}
BigInteger ans = Cal(sum);
ans = ans.divide(Cal(ar[i]));
ans = ans.multiply(BigInteger.valueOf(n - c).pow(n - 2 - sum));
ans = ans.multiply(Cal(n - 2)).divide(Cal(sum)).divide(Cal(n - 2 - sum));
System.out.println(ans);
}
}