PKUSC2018 随机游走
PKUSC2018 随机游走
一道融合了较多知识点、但都不是很深的题。(细节顿号)
前置芝士
树上高斯消元,\(min-max\)容斥,\(FMT\)(俗称高位前缀和?)
树上高斯消元比较简单,不多赘述
\(FMT\)好像有很深奥的原理,但是高位前缀和逐位加入贡献的理解完全够用
所以展开叙述一下\(min-max\)容斥
min-max容斥
这种解法主要来源于两条显然的式子
\[min(S)=\sum_{T \subseteq S} (-1)^{|T|-1} \times max(T)
\]
\[max(S)=\sum_{T \subseteq S} (-1)^{|T|-1} \times min(T)
\]
下面给出一种抽象证明
对于\(min(S)\) 在计算式子右侧时 仅会出现一次 即\(max(S)==T\)
对于其他的 \(max(T)\),必然可以找到一个小于 \(max(T)\)的数
在一种方案中被取,另一种方案中不被取,而这两个方案中其他数的取舍方式相同
于是这两种方式的\(max\)相同,而其中取得数个数不同,即容斥系数互为相反数,就抵消了
Solve
看完题意,直接计算\(max\)显然很困难,但是通过 式\(2\) 就可以转化为 计算\(min\)
对于一次游走,只要碰到\(S\)中的一个点就可以直接结束,显然简单的多
同时问题就转化为了一个可以用树上高斯消元的较简单的过程
再观察到 \(min-max\)容斥的两条式子都是对子集求和,考虑用到\(FWT\)快速求和
代码环节
#include<bits/stdc++.h>
using namespace std;
#define Mod(x) (x>=P)&&(x-=P)||(x<0)&&(x+=P)
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
#define erep(i,a) for(int i=hd[a];i;i=nxt[i])
typedef long long ll;
void Max(int &x,int y){(x<y)&&(x=y);}
void Min(int &x,int y){(x>y)&&(x=y);}
bool vio;
char IO;
int rd(int res=0){
bool f=0;
while(IO=getchar(),IO<48||IO>57)
f|=IO=='-';
do res=(res<<1)+(res<<3)+(IO^48);
while(IO=getchar(),isdigit(IO));
return f?-res:res;
}
const int P=998244353;
const int M=1e6+10;
int nxt[M],hd[M],to[M],deg[M],ecnt;
int f[M],A[20],B[20],pc[M];
int n,q,rt,T;
void Add(int a,int b){
nxt[++ecnt]=hd[a],to[hd[a]=ecnt]=b;
++deg[a];
}
int Pow(int a,int b){
int res=1;
for(Mod(a);b;b>>=1,a=1ll*a*a%P)
if(b&1)res=1ll*res*a%P;
return res;
}
void dfs(int x,int f){
if(T>>(x-1)&1)return ;
int sumA=0,sumB=0;
erep(i,x){
int y=to[i];
if(y==f)continue;
dfs(y,x);
sumA+=A[y],Mod(sumA);
sumB+=B[y],Mod(sumB);
}
int tmp=Pow(deg[x]-sumA,P-2);
A[x]=tmp;
B[x]=1ll*(deg[x]+sumB)*tmp%P;
}
bool let;
int main(){
cerr<<(&vio-&let)/1024.0/1024<<endl;
n=rd(),q=rd(),rt=rd();
rep(i,2,n){
int a=rd(),b=rd();
Add(a,b),Add(b,a);
}
int S=(1<<n)-1;
rep(i,1,S){
memset(A,0,sizeof A);
memset(B,0,sizeof B);
T=i,dfs(rt,0);
pc[i]=pc[i>>1]+(i&1);
f[i]=(pc[i]&1?1:-1)*B[rt],Mod(f[i]);
}
for(int i=1;i<S;i<<=1)
for(int j=0;j<S;j+=i*2)
rep(k,j,j+i-1)f[k+i]+=f[k],Mod(f[k+i]);
while(q--){
int s=0;
rep(i,1,rd())s|=1<<(rd()-1);
printf("%d",f[s]);
}
return 0;
}
原谅我的懒人开数组法

浙公网安备 33010602011771号