不知道为什么做的人很少,其实是一道思维题,想通了很简单,另外吐槽一下这道题坑人的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;
}