不知道为什么做的人很少,其实是一道思维题,想通了很简单,另外吐槽一下这道题坑人的m,竟然里面的重要城市可能会重复,这不是存心害人吗?我就不知道谁平常说话说有m个重要城市还能有重复的,当时没看见搞了我20分钟检查,真的坑。
并没有什么技术性难题,完全是思想,考虑一个重要城市,假如子树大小为k,那么会发现其实有这个城市相比于没有这个城市只是让答案除k了,为什么呢?因为如果没有这个城市,不管这个城市选什么值我们都会在后面的城市得到一系列的排列,而这些值都是等价的,所以这个城市选哪个值并不影响后面有多少个结果,那么如果有了这个城市,那么只有最大值在这个城市的时候才是对的,所以相比于没有限制,当然是结果除k了,所以初始情况n!种,再除以所有重要城市子树数量之积即可。数量的逆元和阶乘的模都可以o(n)打表得到(要有些技巧)。
贴代码
# include <stdio.h> # include <string.h> # include <vector> using namespace std; typedef long long ll; const int MAX_N = 1e5 + 5; const int Mod = 1e9 + 7; bool used[MAX_N]; vector<int> G[MAX_N]; int d[MAX_N]; ll jie[MAX_N + 1], ni[MAX_N + 1]; int N, M; int dfs(int prv , int x) { int ans = 1, i; for(i = 0 ; i < G[x].size() ; i++) { int t = G[x][i]; if(t == prv) continue; ans += dfs(x , t); } return d[x] = ans; } void solve() { dfs(-1 , 0); ll ans = jie[N]; //printf("haha%d\n", d[0]); memset(used , 0 , sizeof(used)); int i, c; for(i = 0 ; i < M ; i++) { scanf("%d", &c); c--; if(!used[c]) { used[c] = 1; ans = ans * ni[d[c]] % Mod; } } printf("%lld\n", ans); } int main() { jie[0] = jie[1] = 1; ni[0] = ni[1] = 1; int i; for(i = 2 ; i <= MAX_N ; i++) jie[i] = jie[i - 1] * i % Mod; for(i = 2 ; i <= MAX_N ; i++) ni[i] = ni[Mod % i] * (Mod - Mod / i) % Mod; //printf("%lld\n", 19261 * ni[19261] % Mod); while(~scanf("%d %d", &N, &M)) { int i, t, s; for(i = 0 ; i < N - 1 ; i++) { scanf("%d %d", &t, &s); t--; s--; G[t].push_back(s); G[s].push_back(t); } solve(); for(i = 0 ; i < N ; i++) G[i].clear(); } return 0; }