soj考试2

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 }
原文地址:https://www.cnblogs.com/Scx117/p/9098809.html