题目链接:http://codeforces.com/contest/1111/problem/E
题目大意:
给定一棵树,和若干个询问,
k,m,r和k个数,问把这k个点分成至多m组
且每组中的点集没有父节点与子节点的关系,
其方案数有多少种。
题目分析:
这道题我是看了别人的思路才懂的,
果然还是自己太弱了呀。。。
先简化问题,如果根是固定的如何考虑,
那么观察到m的数据范围去考虑DP计数,
dp(i,j)代表i个数分成j组有多少种方案,
转移方程:dp(i,j)=dp(i-1,j-1)+dp(i-1,j)*(j-f[i]),
其中f[i]代表的是i节点对于1根有多少个父节点,
因为这f[i]个父节点肯定安排在不同的集合中,
那么根据DFS序性质,我们可以利用树状数组统计父节点的个数,
但只能对于特定的根,如果根变换呢?
考虑路径差,特定的点其到特定的根的路径上的权重,
即树上任意两点之间路径的权值和,我们利用LCA来辅助转换。
所以就是对于每个查询,我们不难统计出每个点的f[i],这样状态转移方程的
条件就有了。
我的想法是直接按DFS再搜一遍,遇到其k个点之一就刷新状态数组,
但是我看的这位博主另有想法,它直接把f数组排序然后对这个数组进行DP了,
DP的性质是无后效性,而子节点不会比父节点的f值来的大,所以对于
任意一个互斥对都满足了更新顺序。最后一些细节就是滚动数组,还有DP数组开int再类型转换取模优化下,
我开long long被T了。
#include<bits/stdc++.h>
using namespace std;
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define ll long long
#define mst(a,b) memset((a),(b),sizeof(a))
const int mod=1e9+7;
const int maxn=1e5+10;
const int ub=1e6;
const double inf=1e-4;
ll powmod(ll x,ll y){ll t; for(t=1;y;y>>=1,x=x*x%mod) if(y&1) t=t*x%mod; return t;}
ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}
/*
题目大意:
给定一棵树,和若干个询问,
k,m,r和k个数,问把这k个点分成至多m组
且每组中的点集没有父节点与子节点的关系,
其方案数有多少种。
题目分析:
这道题我是看了别人的思路才懂的,
果然还是自己太弱了呀。。。
先简化问题,如果根是固定的如何考虑,
那么观察到m的数据范围去考虑DP计数,
dp(i,j)代表i个数分成j组有多少种方案,
转移方程:dp(i,j)=dp(i-1,j-1)+dp(i-1,j)*(j-f[i]),
其中f[i]代表的是i节点对于1根有多少个父节点,
因为这f[i]个父节点肯定安排在不同的集合中,
那么根据DFS序性质,我们可以利用树状数组统计父节点的个数,
但只能对于特定的根,如果根变换呢?
考虑路径差,特定的点其到特定的根的路径上的权重,
即树上任意两点之间路径的权值和,我们利用LCA来辅助转换。
所以就是对于每个查询,我们不难统计出每个点的f[i],这样状态转移方程的
条件就有了。
我的想法是直接按DFS再搜一遍,遇到其k个点之一就刷新状态数组,
但是我看的这位博主另有想法,它直接把f数组排序然后对这个数组进行DP了,
DP的性质是无后效性,而子节点不会比父节点的f值来的大,所以对于
任意一个互斥对都满足了更新顺序。
最后一些细节就是滚动数组,还有DP数组开int再类型转换取模优化下,
我开long long被T了。
*/
int n,q,x,y;
///前式链向星
vector<int> g[maxn];
int pl[maxn],pr[maxn],bit[maxn],tot;///深度
inline void refresh(int x,int d){
for(;x<maxn;bit[x]+=d,x+=x&-x);
}
inline int sum(int x){
int ret=0;for(;x;ret+=bit[x],x-=x&-x);return ret;
}
int st[maxn][20],dep[maxn];
inline void dfs(int u,int pre){
pl[u]=++tot;
dep[u]=dep[pre]+1;
st[u][0]=pre;rep(i,1,20) st[u][i]=st[st[u][i-1]][i-1];
for(int i=0;i<g[u].size();i++){
int v=g[u][i];
if(v==pre) continue;
dfs(v,u);
}
pr[u]=tot;
}
inline int lca(int u,int v){
if(u==v) return u;
if(dep[u]<dep[v]) swap(u,v);
for(int i=19;i>=0;i--) if(dep[st[u][i]]>=dep[v])
u=st[u][i];
if(u==v) return u;
for(int i=19;i>=0;i--) if(st[u][i]!=st[v][i])
u=st[u][i],v=st[v][i];
return st[u][0];
}
int dp[maxn];
int k,m,r,a[maxn],f[maxn],vis[maxn];
int main(){
scanf("%d%d",&n,&q);
rep(i,0,n-1){
scanf("%d%d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}
tot=0;dfs(1,0);
rep(i,0,q){
scanf("%d%d%d",&k,&m,&r);
rep(i,1,k+1){
scanf("%d",&a[i]);
vis[a[i]]=1;
refresh(pl[a[i]],1);
refresh(pr[a[i]]+1,-1);
}
int tmp=sum(pl[r]);
rep(i,1,k+1){
int LCA=lca(r,a[i]);
f[i]=sum(pl[a[i]])+tmp-2*sum(pl[LCA])+vis[LCA]-1;///减去自己
}
rep(i,1,k+1){
vis[a[i]]=0;
refresh(pl[a[i]],-1);
refresh(pr[a[i]]+1,1);
}
if(f[k]>=m) {puts("0");continue;}
sort(f+1,f+1+k);
mst(dp,0),dp[0]=1;
rep(i,1,k+1) for(int j=min(i,m);j>=0;j--){
if(j<=f[i]) dp[j]=0;
else dp[j]=((ll)dp[j-1]+(ll)dp[j]*(j-f[i])%mod)%mod;
}
ll ans=0;rep(i,1,m+1) (ans+=dp[i])%=mod;
printf("%lld\n",ans);
}
return 0;
}