T1:子图
给你一棵带点权的树,对于所有i∈[1,m],问树上是否存在连通子图的权值和=i?
n<=3000,m<=100000。
朴素的背包树形dp有nm的复杂度,bitset也无处优化。
但是从根往叶子考虑,必经根的连通块权值和很好用bitset维护。每个点的bitset表示经过rt和该点子树及之前已访问点的连通块权值和可能值。类似树形dp的合并。
树分治。时间复杂度O(n^2logn/w)
//注意每次需要重新计算size,设置done标记。
标程:
1 #include<bits/stdc++.h> 2 using namespace std; 3 int read() 4 { 5 int x=0,f=1;char ch=getchar(); 6 while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} 7 while (ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); 8 return x*f; 9 } 10 const int N=3005; 11 const int M=100005; 12 struct node{int to,next;}num[N*2]; 13 int cnt,head[N],u,v,sz[N],Max[N],rt,n,m,Sz,w[N],done[N]; 14 bitset<M> bit[N],ans; 15 void add(int x,int y) 16 {num[++cnt].to=y;num[cnt].next=head[x];head[x]=cnt;} 17 void find_rt(int x,int fa) 18 { 19 sz[x]=1;Max[x]=0; 20 for (int i=head[x];i;i=num[i].next) 21 if (num[i].to!=fa&&!done[num[i].to]) 22 { 23 find_rt(num[i].to,x); 24 sz[x]+=sz[num[i].to]; 25 Max[x]=max(Max[x],sz[num[i].to]); 26 } 27 Max[x]=max(Max[x],Sz-sz[x]); 28 if (Max[x]<Max[rt]) rt=x; 29 } 30 void work(int x,int fa) 31 { 32 bit[x]=bit[fa]<<w[x];sz[x]=1;//sz需要重新计算 33 for (int i=head[x];i;i=num[i].next) 34 if (num[i].to!=fa&&!done[num[i].to]) 35 { 36 work(num[i].to,x); 37 sz[x]+=sz[num[i].to]; 38 bit[x]|=bit[num[i].to]; 39 } 40 } 41 void solve(int u) 42 { 43 done[u]=1;work(u,0);ans|=bit[u]; 44 for (int i=head[u];i;i=num[i].next) 45 if (!done[num[i].to]) 46 { 47 rt=0;Sz=sz[num[i].to]; 48 find_rt(num[i].to,u); 49 solve(rt); 50 } 51 } 52 int main() 53 { 54 int T=read(); 55 bit[0][0]=1; 56 while (T--) 57 { 58 n=read();m=read(); 59 cnt=0;for (int i=1;i<=n;i++) bit[i].reset(),head[i]=done[i]=0; 60 for (int i=1;i<n;i++) u=read(),v=read(),add(u,v),add(v,u); 61 for (int i=1;i<=n;i++) w[i]=read(); 62 Max[0]=Sz=n;ans.reset(); 63 rt=0;find_rt(1,-1); 64 solve(rt); 65 for (int i=1;i<=m;i++) printf("%d",(int)ans[i]); 66 puts(""); 67 } 68 return 0; 69 }
T2:结婚
一共有n户,每一户有ai个男孩和bi个女孩。n户的男孩总数=女孩总数。
问男孩和女孩结成n对且没有一对是同户的方案数%998244353?n<=1e5。
朴素dp:$dp[l+a-j-k]+=dp[l][i]*dbinom{a}{j}*dbinom{b}{j}*(gsum-bsum+l)^{underline{j}}*l^{underline{k}}$
i为枚举到的住户。a,b为对应的男孩、女孩数。l表示当前还剩l个男孩未配对。每次都在前面找不冲突的女孩配对。j、k分别表示这一户的男孩、女孩配上对的数量。gsum表示之前的女孩总数,bsum表示之前的男孩总数。
答案即为dp[0][n+1]。
容斥,设g为至少有i对男女同户的方案数。Ans=sigma((-1)^i*g[i])。
对每一户设置生成函数:F(i)=c0+c1*x+c2*x^2+...,x^i表示在这一户中选i对男女配对的方案数,组合数预处理。
F(1)*F(2)*...*F(n)的第i项系数则是全局中选i对男女同户的方案数,*(n-i)!即为g[i]。
分治fft加速O(nlog^2(n))。
//写fft的时候尽量用pos[i]<i->swap(a[i],a[pos[i]])。
//1<<l要大于原n,否则枚举时是0~n-1,最后一个算不到。
标程:
1 #include<bits/stdc++.h> 2 #define P pair<int,int> 3 #define fir first 4 #define sec second 5 using namespace std; 6 typedef long long ll; 7 const int mod=998244353; 8 const int root=31; 9 const int N=300005; 10 int jc[N],inv[N],sum,ans,l,_n,w[N],pos[N],tmp[N],n,k1,k2,a,b,len[N],f[N],g[N]; 11 vector<int> vec[N]; 12 set<P> q; 13 int read() 14 { 15 int x=0,f=1;char ch=getchar(); 16 while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} 17 while (ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); 18 return x*f; 19 } 20 int ksm(int x,int y) 21 { 22 int res=1; 23 while (y) {if (y&1) res=(ll)res*x%mod; x=(ll)x*x%mod;y>>=1;} 24 return res; 25 } 26 int c(int n,int m) {return (ll)jc[n]*inv[m]%mod*inv[n-m]%mod;} 27 28 void init() 29 { 30 l=0;while ((1<<l)<=_n) l++;//1<<l必须要大于原来的n 31 _n=1<<l; int ww=ksm(root,1<<23-l); 32 w[0]=1; 33 for (int i=1;i<=_n;i++) 34 w[i]=(ll)ww*w[i-1]%mod,pos[i]=((i&1)<<l-1)|(pos[i>>1]>>1); 35 } 36 void fft(int *a) 37 { 38 for (int i=0;i<_n;i++) if (pos[i]<i) swap(a[i],a[pos[i]]);//swap可以避免多用一个数组和慢得要死的memset 39 int len=1,id=_n; 40 for (int i=0;i<l;i++) 41 { 42 int g=w[id>>=1]; 43 for (int j=0;j<_n;j+=2*len) 44 for (int k=0,ww=1;k<len;k++) 45 { 46 int L=a[j+k],R=(ll)a[j+len+k]*ww%mod; 47 a[j+k]=((ll)L+R)%mod;a[j+len+k]=((ll)L-R+mod)%mod; 48 ww=(ll)ww*g%mod; 49 } 50 len<<=1; 51 } 52 } 53 int main() 54 { 55 n=read(); 56 jc[0]=inv[0]=jc[1]=inv[1]=1; 57 for (int i=2;i<=100000;i++) jc[i]=(ll)jc[i-1]*i%mod,inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod; 58 for (int i=2;i<=100000;i++) inv[i]=(ll)inv[i-1]*inv[i]%mod; 59 for (int i=1;i<=n;i++) 60 { 61 a=read(),b=read();sum+=a; 62 q.insert(P(len[i]=min(a,b)+1,i)); 63 for (int j=0;j<=min(a,b);j++) 64 vec[i].push_back((ll)c(a,j)*c(b,j)%mod*jc[j]%mod); 65 } 66 for (int i=1;i<n;i++) 67 { 68 k1=q.begin()->sec;q.erase(q.begin()); 69 k2=q.begin()->sec;q.erase(q.begin()); 70 memcpy(f,vec[k1].data(),len[k1]*4); 71 memcpy(g,vec[k2].data(),len[k2]*4); 72 vec[k1].clear();vec[k2].clear(); 73 74 _n=len[k1]+len[k2]-1; 75 init();fft(f);fft(g); 76 for (int j=0;j<_n;j++) f[j]=(ll)f[j]*g[j]%mod; 77 fft(f);reverse(f+1,f+_n); int inv_n=ksm(_n,mod-2); 78 for (int j=0;j<_n;j++) vec[k1].push_back((ll)f[j]*inv_n%mod),f[j]=g[j]=0; 79 q.insert(P(len[k1]=_n,k1)); 80 } 81 int u=q.begin()->sec; 82 for (int i=0;i<=sum;i++) 83 { 84 int x=(ll)vec[u][i]*jc[sum-i]%mod; 85 if (i&1) ans=((ll)ans-x+mod)%mod;else ans=((ll)ans+x)%mod; 86 } 87 printf("%d ",ans); 88 return 0; 89 }
T3:构图
给你两棵n个节点的树A,B。有一个m个点的图,一开始没有边。
A和B上的每个节点都有一个信息ai,bi,表示取了这个点,就在图上ai-bi连边。
对于1~n,询问取A树上和B树上1->i的路径上所有点的信息更新图,图中有多少个连通块?
n,m<=1e4.
对A树按深度分块。维护块顶到根路径的点信息扔进可退回并查集。
每走一个块就对B树整体dfs,边走边处理点信息。如果B树中走到当前A树块中的点,那么暴力加入该点在A树中到块顶的链信息。
时间复杂度O(n^1.5logn)。对于A树每个点最多暴力跳n^0.5,所有点就是O(n^1.5)。B树中在n^0.5个块中都跑一遍全树dfs,O(n^1.5)。并查集按秩合并logn。
//注意分块的时候从底往上分块,若从上往下碰到扫把型会被卡掉。
//注意判断dfs序。要在块端点子树内才会被该端点管辖。
标程:
1 #include<bits/stdc++.h> 2 using namespace std; 3 int read() 4 { 5 int x=0,f=1;char ch=getchar(); 6 while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} 7 while (ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); 8 return x*f; 9 } 10 const int N=10005; 11 vector<int> vec_a[N],vec_b[N]; 12 int f[N],sz[N],top,tt,L,R,dep[N],Fa[N],n,ans[N],vis[N],u,v,au[N],av[N],bu[N],bv[N],Dfn,dfn[N],m; 13 struct node{int x,y,sz,bf;}sta[N*2]; 14 int find(int x){return x==f[x]?x:find(f[x]);} 15 void merge(int x,int y) 16 { 17 x=find(x);y=find(y); 18 if (x==y) return; 19 if (sz[x]>sz[y]) swap(x,y); 20 sta[++top].x=x;sta[top].y=y;sta[top].sz=sz[y];sta[top].bf=f[x]; 21 f[x]=y;if (sz[x]==sz[y]) sz[y]++; 22 } 23 void Re(int x) 24 { 25 while (top>x) 26 { 27 f[sta[top].x]=sta[top].bf; 28 sz[sta[top].y]=sta[top].sz; 29 top--; 30 } 31 } 32 void work(int x) 33 { 34 int t=top; 35 for (int i=x;i!=tt;i=Fa[i]) merge(au[i],av[i]); 36 ans[x]=m-top;vis[x]=1; 37 Re(t); 38 } 39 void dfs_B(int x,int fa) 40 { 41 int t=top;merge(bu[x],bv[x]); 42 if (!vis[x]&&dfn[x]>=dfn[tt]) work(x); 43 for (int i=0;i<vec_b[x].size();i++) 44 if (vec_b[x][i]!=fa) dfs_B(vec_b[x][i],x); 45 Re(t); 46 } 47 void dfs_A(int x,int fa) 48 { 49 int t=top; merge(au[x],av[x]); 50 dep[x]=1;dfn[x]=++Dfn; 51 for (int i=0;i<vec_a[x].size();i++) 52 if (vec_a[x][i]!=fa) 53 { 54 dfs_A(vec_a[x][i],x); 55 dep[x]=max(dep[x],dep[vec_a[x][i]]+1); 56 } 57 if (dep[x]==100||x==1) tt=x,dfs_B(1,-1),dep[x]=0; 58 Re(t); 59 } 60 int main() 61 { 62 int T=read(); 63 while (T--) 64 { 65 n=read();m=read();Dfn=top=0; 66 for (int i=1;i<=n;i++) vec_a[i].clear(),vec_b[i].clear(),vis[i]=dfn[i]=0; 67 for (int i=1;i<=n;i++) au[i]=read(),av[i]=read(); 68 for (int i=1;i<n;i++) u=read(),v=read(),vec_a[u].push_back(v),vec_a[v].push_back(u),Fa[v]=u; 69 for (int i=1;i<=n;i++) bu[i]=read(),bv[i]=read(); 70 for (int i=1;i<n;i++) u=read(),v=read(),vec_b[u].push_back(v),vec_b[v].push_back(u); 71 for (int i=1;i<=m;i++) f[i]=i,sz[i]=1; 72 dfs_A(1,-1); 73 for (int i=1;i<=n;i++) assert(vis[i]==1); 74 for (int i=1;i<=n;i++) printf("%d ",ans[i]); 75 } 76 return 0; 77 }