A:
我们尝试给每个点划分联通块
定义一个联通块的位置是它里面深度最浅的点
那么一个点要么属于他某个祖先的联通块,要么自己这里有一个联通块
于是可以做个dp
但是dp状态里要有个当前的最大值,状态数就
n2
n
2
了
我们可以先二分,就不用记录当前最大的联通块大小了
然后记
f[i][0/1]
f
[
i
]
[
0
/
1
]
表示联通块位置在
i
i
,这个位置的联通块中 没有/有 标记点,子树里其他联通块合法,这个联通块的最小大小
记
g[i][0/1]
g
[
i
]
[
0
/
1
]
表示联通块在
i
i
的祖先某个位置,和
i
i
的子树里属于这个联通块的点中 没有/有 标记点,子树里其他联通块合法,这个联通块的最小大小
复杂度
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
inline void read(int &x)
{
char c; while(!((c=getchar())>='0'&&c<='9'));
x=c-'0';
while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
const int maxn = 210000;
int n,K;
int s[maxn];
struct edge{int y,nex;}a[maxn<<1]; int len,fir[maxn];
inline void ins(const int x,const int y){a[++len]=(edge){y,fir[x]};fir[x]=len;}
int u;
int f[maxn][2],g[maxn][2];
void upd(int &a,const int &b){if(a==-1||a>b)a=b;}
void dp(const int x,const int fa)
{
f[x][0]=f[x][1]=g[x][0]=g[x][1]=-1;
f[x][s[x]]=g[x][s[x]]=1;
for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(y!=fa)
{
dp(y,x);
int tf[2],tg[2];
tf[0]=f[x][0],tf[1]=f[x][1],tg[0]=g[x][0],tg[1]=g[x][1];
f[x][0]=f[x][1]=g[x][0]=g[x][1]=-1;
for(int i=0;i<2;i++)
{
if(f[y][1]!=-1)
{
if(tf[i]!=-1) upd(f[x][i],tf[i]);
if(tg[i]!=-1) upd(g[x][i],tg[i]);
}
if(g[y][0]!=-1)
{
int c=g[y][0];
if(tf[i]!=-1) upd(f[x][i],tf[i]+c);
if(tg[i]!=-1) upd(g[x][i],tg[i]+c);
}
if(g[y][1]!=-1)
{
int c=g[y][1];
if(tf[i]!=-1) upd(f[x][1],tf[i]+c);
if(tg[i]!=-1) upd(g[x][1],tg[i]+c);
}
}
}
for(int i=0;i<2;i++)
{
if(f[x][i]>u) f[x][i]=-1;
if(g[x][i]>u) g[x][i]=-1;
}
}
int judge(int mid)
{
u=mid;
dp(1,0);
return f[1][1]!=-1&&f[1][1]<=u;
}
int main()
{
freopen("deep.in","r",stdin);
freopen("deep.out","w",stdout);
read(n); read(K);
for(int i=1;i<n;i++)
{
int x,y; read(x),read(y);
ins(x,y),ins(y,x);
}
for(int i=1;i<=K;i++)
{
int x; read(x);
s[x]=1;
}
int l=1,r=n;
while(l<=r)
{
int mid=(l+r)>>1;
if(judge(mid)) r=mid-1;
else l=mid+1;
}
printf("%d\n",r+1);
return 0;
}
B:
求
n
n
个点的无向连通图中联通块个数的次方的和
考场上推二项式展开,写了个
O(mnlog2n+T)
O
(
m
n
l
o
g
2
n
+
T
)
的分治FFT
慢的飞起但是出题人放过了这个复杂度
然后有一种更优秀的用斯特林数推的 O(mnlogn+Tm) O ( m n l o g n + T m ) 的做法
xn
x
n
的意义是n个球,每个球选x个盒子中的一个盒子装,允许有空盒子
于是有
xn=∑xi=0{ni}(xi)i!
x
n
=
∑
i
=
0
x
{
i
n
}
(
x
i
)
i
!
于是我们可以设 f[i][j] f [ i ] [ j ] 表示对于一个有 x x 个联通块的图,我们定义它的价值为,求所有 i i 个点的图的价值和
我们考虑这个东西的组合意义,即从 n n 个数中有序的选出组数的方案数
根据这个列出
f
f
的转移式
其中 g[k] g [ k ] 表示 k k 个点的无向连通图个数,可以用图的总数减( i i 个点的无向不连通图个数)算,可以分治FFT做到或者多项式求逆做到 O(nlogn) O ( n l o g n ) ,具体做法不在此赘述
发现 f[i][j] f [ i ] [ j ] 的dp式只和 f[i][j−1] f [ i ] [ j − 1 ] 有关,我们可以分层dp,每层用一个FFT
最终的答案 ans=∑mj=0{mj}f[n][j] a n s = ∑ j = 0 m { j m } f [ n ] [ j ]
复杂度就可以做到 O(mnlogn+Tm) O ( m n l o g n + T m )
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 120005;
const int maxm = 16;
const int mod = 998244353;
inline void add(int &a,const int &b){a+=b;if(a>=mod)a-=mod;}
inline void dec(int &a,const int &b){a-=b;if(a<0)a+=mod;}
int pw(int x,int k)
{
int re=1;
for(;k;k>>=1,x=(ll)x*x%mod) if(k&1)
re=(ll)re*x%mod;
return re;
}
int inv(int x){ return pw(x,mod-2); }
int s[maxn],invs[maxn],num[maxn],dp[maxm][maxm];
void pre()
{
s[0]=1; for(int i=1;i<maxn;i++) s[i]=(ll)s[i-1]*i%mod;
invs[maxn-1]=inv(s[maxn-1]);
for(int i=maxn-2;i>=0;i--) invs[i]=(ll)invs[i+1]*(i+1)%mod;
for(int i=0;i<maxn;i++) num[i]=pw(2,(ll)i*(i-1)/2ll%(mod-1));
dp[0][0]=1;
for(int i=1;i<maxm;i++) for(int j=1;j<=i;j++)
dp[i][j]=(dp[i-1][j-1]+(ll)dp[i-1][j]*j%mod)%mod;
}
int C(int i,int j){ return i<j?0:(ll)s[i]*invs[j]%mod*invs[i-j]%mod; }
namespace NTT
{
int N,ln;
int id[maxn],w[maxn];
void pre(int n)
{
N=1,ln=0;
while(N<=n) N<<=1,ln++;
for(int i=0;i<N;i++) id[i]=id[i>>1]>>1|(i&1)<<(ln-1);
w[0]=1,w[1]=pw(3,(mod-1)/N);
for(int i=2;i<=N;i++) w[i]=(ll)w[i-1]*w[1]%mod;
}
void DFT(int f[],int sig)
{
for(int i=1;i<N;i++) if(i<id[i]) swap(f[i],f[id[i]]);
for(int m=2;m<=N;m<<=1)
{
int t=m>>1,tt=N/m;
for(int i=0;i<t;i++)
{
int wn=sig==1?w[i*tt]:w[N-i*tt];
for(int j=i;j<N;j+=m)
{
int tx=f[j],ty=(ll)f[j+t]*wn%mod;
f[j]=tx; add(f[j],ty);
f[j+t]=tx; dec(f[j+t],ty);
}
}
}
if(sig==-1)
{
int invn=inv(N);
for(int i=0;i<N;i++) f[i]=(ll)f[i]*invn%mod;
}
}
int f[maxn],g[maxn],tf[maxn];
void main(int n,int m)
{
pre(n+m);
for(int i=n+1;i<N;i++) f[i]=0;
for(int i=m+1;i<N;i++) g[i]=0;
if(N<=16)
{
for(int i=0;i<N;i++) tf[i]=f[i],f[i]=0;
for(int i=0;i<=n;i++) for(int j=0;j<=m;j++)
add(f[i+j],(ll)tf[i]*g[j]%mod);
}
else
{
DFT(f,1); DFT(g,1);
for(int i=0;i<N;i++) f[i]=(ll)f[i]*g[i]%mod;
DFT(f,-1);
}
}
}
int n,m;
int f[maxn][maxm],g[maxn],h[maxn];
void solveg(int l,int r)
{
if(l==r)
{
if(l) h[l]=(ll)h[l]*s[l-1]%mod;
g[l]=(num[l]-h[l]+mod)%mod;
return;
}
int mid=(l+r)>>1;
solveg(l,mid);
int tn=mid-l,tm=r-l;
NTT::f[0]=0; for(int i=max(1,l);i<=mid;i++) NTT::f[i-l]=(ll)g[i]*invs[i-1]%mod;
for(int i=0;i<=tm;i++) NTT::g[i]=(ll)num[i]*invs[i]%mod;
NTT::main(tn,tm);
for(int i=mid+1;i<=r;i++) add(h[i],NTT::f[i-l]);
solveg(mid+1,r);
}
int T,un,um;
int op[1100][2];
int nowj;
void solvef()
{
NTT::g[0]=0; for(int i=1;i<=un;i++) NTT::g[i]=(ll)g[i]*invs[i]%mod;
for(int i=0;i<=un;i++) NTT::f[i]=(ll)f[i][nowj-1]*invs[i]%mod;
NTT::main(un,un);
for(int i=0;i<=un;i++) f[i][nowj]=(ll)NTT::f[i]*s[i]%mod;
}
int main()
{
freopen("dark.in","r",stdin);
freopen("dark.out","w",stdout);
pre();
scanf("%d",&T);
for(int i=1;i<=T;i++)
{
scanf("%d%d",&op[i][0],&op[i][1]);
un=max(un,op[i][0]),um=max(um,op[i][1]);
}
h[0]=0,g[0]=1;
solveg(0,un);
/*for(int i=1;i<maxn;i++)
{
for(int j=1;j<i;j++) add(h[i],(ll)C(i-1,j-1)*g[j]%mod*num[i-j]%mod);
g[i]=(num[i]-h[i]+mod)%mod;
}*/
for(int i=0;i<=un;i++) f[i][0]=num[i];
//for(int j=0;j<maxm;j++) for(int k=0;k<=j;k++) add(tf[0][j],C(j,k)*f[0][k]);
for(nowj=1;nowj<=um;nowj++) solvef();
/*for(int i=1;i<2001;i++) for(int j=0;j<maxm;j++)
{
for(int k=1;k<=i;k++) add(f[i][j],(ll)C(i-1,k-1)*g[k]%mod*tf[i-k][j]%mod);
for(int k=0;k<=j;k++) add(tf[i][j],(ll)C(j,k)*f[i][k]%mod);
}*/
for(int i=1;i<=T;i++)
{
int ans=0;
for(int j=0;j<=op[i][1];j++) add(ans,(ll)dp[op[i][1]][j]*f[op[i][0]][j]%mod);
printf("%d\n",ans);
}
return 0;
}
C:
题解说是后缀平衡树
还套上了很多看起来很nb的技巧
不会
写不动
弃疗