P6651 「SWTR-5」Chain
前言
其实感觉题目描述不太对,应该改为禁用一个点(因为新的链不算)。
正言
不难发现一个最简单的 dp。即
考虑我们如何删点,其实就是删除经过
可是这样计算显然有重复,一般去重我们考虑容斥,但是显然不能正常容斥。所以考虑如何在树上快速的进行容斥。
考虑对于每个点去重,而我们先用拓扑序钦定一个顺序,使得后面的点不能走前面走过的路径。所以对于在
注意我们并不是用原来的
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pb push_back
#define eb emplace_back
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const ll MOD=1e9+7;
// head
const int N=2e3+5;
vector<vector<int>> G(N),G1(N);
int du[N],du1[N];
int f[N],g[N],h[N][N];
int id[N],cnt;
int c[N],d[N];
bool cmp(int p,int q) {return id[p]<id[q];}
signed main()
{
cin.tie(nullptr);cout.tie(nullptr);
ios::sync_with_stdio(false);
int n,m;cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;cin>>u>>v;
G[u].pb(v);du[v]++;
G1[v].pb(u);du1[u]++;
}
queue<int> Q;
for(int i=1;i<=n;i++) h[i][i]=1;
for(int i=1;i<=n;i++) if(!du[i]) {Q.push(i);f[i]=1;}
while(!Q.empty())
{
int u=Q.front();
id[u]=++cnt;
Q.pop();
for(auto v:G[u]){
du[v]--;
(f[v]+=f[u])%=MOD;
for(int i=1;i<=n;i++) (h[i][v]+=h[i][u])%=MOD;
if(!du[v]) Q.push(v);
}
}
int sum=0;
for(int i=1;i<=n;i++) if(!du1[i]) {Q.push(i);g[i]=1,(sum+=f[i])%=MOD;}
while(!Q.empty())
{
int u=Q.front();
Q.pop();
for(auto v:G1[u]){
du1[v]--;
(g[v]+=g[u])%=MOD;
if(!du1[v]) Q.push(v);
}
}
int q;cin>>q;
while(q--)
{
int k;cin>>k;
for(int i=1;i<=k;i++) cin>>c[i];
sort(c+1,c+k+1,cmp);
int ans=sum;
for(int i=1;i<=k;i++) d[i]=f[c[i]];
for(int i=1;i<=k;i++){
for(int j=1;j<i;j++){
(d[i]-=d[j]*h[c[j]][c[i]]%MOD-MOD)%=MOD;
}
(ans-=d[i]*g[c[i]]%MOD-MOD)%=MOD;
}
cout<<ans<<endl;
}
}