luogu P5643 [PKUWC2018]随机游走
题面传送门
发现PKU很稀饭出高消的题目?
每个点都经过一次不太好处理考虑Min-Max容斥,转化成一个点集中的点经过一个点的最早时间就比较可做一点。
发现可以直接高消,时间复杂度\(O(n^32^n)\)一眼不可过。但是这个是个树形结构,可以用一种特殊的主元法,也就是每个点用父亲表示出来,然后在根节点解答案。容易发现可以用每个点对应的方程做到。于是就可以\(O(n2^n\log mod)\)预处理了。
但是还有个问题是询问,如果每个询问暴力查是\(O(Q2^n)\)的也不太能过。考虑式子\(\sum\limits_{S\subset T}{(-1)^{|S|+1}f_S}\)和\(T\)没有半毛钱关系于是可以FMT。就可以做到\(O(n2^n+Qn)\)了。
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define ll long long
#define db double
#define lb long db
#define N (20+5)
#define M ((1<<20)+5)
#define Ks (12+5)
#define mod 998244353
#define Mod (mod-1)
#define eps (1e-9)
#define ull unsigned ll
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
#define R(n) (rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;
int n,m,k,x,y,z,T,G[N];ll K[N],B[N],F[M],Inv[N];vector<int> S[N];
I ll mpow(ll x,int y=mod-2){ll Ans=1;while(y) y&1&&(Ans=Ans*x%mod),y>>=1,x=x*x%mod;return Ans;}
I void Make(int x,int La,int W){if(W>>(x-1)&1)return; if(!La){for(int i:S[x]) i^La&&(Make(i,x,W),0);return;}ll Invn=Inv[(int)S[x].size()];K[x]=B[x]=1;for(int i:S[x]) i^La&&(Make(i,x,W),K[x]=(K[x]-K[i]*Invn)%mod,B[x]=(B[i]*Invn+B[x])%mod);B[x]=B[x]*mpow(K[x])%mod;K[x]=Invn*mpow(K[x])%mod;}
int main(){
freopen("1.in","r",stdin);
int i,j,h;scanf("%d%d%d",&n,&m,&T);for(i=1;i<n;i++)scanf("%d%d",&x,&y),S[x].PB(y),S[y].PB(x);for(Inv[0]=i=1;i<=n;i++) Inv[i]=mpow(i);
for(i=1;i<(1<<n);i++){
if(i>>(T-1)&1) continue;Me(K,0);Me(B,0);Make(T,0,i);int Invn=Inv[(int)S[T].size()];K[T]=B[T]=1;
for(int j:S[T]) K[T]=(K[T]-K[j]*Invn)%mod,B[T]=(B[j]*Invn+B[T])%mod;F[i]=(B[T]*mpow(K[T])%mod+mod)%mod;
}
for(i=1;i<(1<<n);i++){x=i;y=-1;while(x) y*=-1,x-=x&-x;F[i]=(F[i]*y+mod)%mod;}
for(i=1;i<=n;i++) for(j=0;j<(1<<n);j+=(1<<i))for(h=j;h<j+(1<<i-1);h++) F[h+(1<<i-1)]=(F[h+(1<<i-1)]+F[h])%mod;
while(m--){z=0;scanf("%d",&x);while(x--) scanf("%d",&y),z|=(1<<y-1);printf("%lld\n",F[z]);}
}