T1:子图

给你一棵带点权的树,对于所有i∈[1,m],问树上是否存在连通子图的权值和=i?

n<=3000,m<=100000。

朴素的背包树形dp有nm的复杂度,bitset也无处优化。

但是从根往叶子考虑,必经根的连通块权值和很好用bitset维护。每个点的bitset表示经过rt和该点子树及之前已访问点的连通块权值和可能值。类似树形dp的合并。

树分治。时间复杂度O(n^2logn/w)

//注意每次需要重新计算size,设置done标记。

标程:

 #include<bits/stdc++.h>
using namespace std;
int read()
{
int x=,f=;char ch=getchar();
while (ch<''||ch>'') {if (ch=='-') f=-;ch=getchar();}
while (ch>=''&&ch<='') x=(x<<)+(x<<)+ch-'',ch=getchar();
return x*f;
}
const int N=;
const int M=;
struct node{int to,next;}num[N*];
int cnt,head[N],u,v,sz[N],Max[N],rt,n,m,Sz,w[N],done[N];
bitset<M> bit[N],ans;
void add(int x,int y)
{num[++cnt].to=y;num[cnt].next=head[x];head[x]=cnt;}
void find_rt(int x,int fa)
{
sz[x]=;Max[x]=;
for (int i=head[x];i;i=num[i].next)
if (num[i].to!=fa&&!done[num[i].to])
{
find_rt(num[i].to,x);
sz[x]+=sz[num[i].to];
Max[x]=max(Max[x],sz[num[i].to]);
}
Max[x]=max(Max[x],Sz-sz[x]);
if (Max[x]<Max[rt]) rt=x;
}
void work(int x,int fa)
{
bit[x]=bit[fa]<<w[x];sz[x]=;//sz需要重新计算
for (int i=head[x];i;i=num[i].next)
if (num[i].to!=fa&&!done[num[i].to])
{
work(num[i].to,x);
sz[x]+=sz[num[i].to];
bit[x]|=bit[num[i].to];
}
}
void solve(int u)
{
done[u]=;work(u,);ans|=bit[u];
for (int i=head[u];i;i=num[i].next)
if (!done[num[i].to])
{
rt=;Sz=sz[num[i].to];
find_rt(num[i].to,u);
solve(rt);
}
}
int main()
{
int T=read();
bit[][]=;
while (T--)
{
n=read();m=read();
cnt=;for (int i=;i<=n;i++) bit[i].reset(),head[i]=done[i]=;
for (int i=;i<n;i++) u=read(),v=read(),add(u,v),add(v,u);
for (int i=;i<=n;i++) w[i]=read();
Max[]=Sz=n;ans.reset();
rt=;find_rt(,-);
solve(rt);
for (int i=;i<=m;i++) printf("%d",(int)ans[i]);
puts("");
}
return ;
}

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,最后一个算不到。

标程:

 #include<bits/stdc++.h>
#define P pair<int,int>
#define fir first
#define sec second
using namespace std;
typedef long long ll;
const int mod=;
const int root=;
const int N=;
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];
vector<int> vec[N];
set<P> q;
int read()
{
int x=,f=;char ch=getchar();
while (ch<''||ch>'') {if (ch=='-') f=-;ch=getchar();}
while (ch>=''&&ch<='') x=(x<<)+(x<<)+ch-'',ch=getchar();
return x*f;
}
int ksm(int x,int y)
{
int res=;
while (y) {if (y&) res=(ll)res*x%mod; x=(ll)x*x%mod;y>>=;}
return res;
}
int c(int n,int m) {return (ll)jc[n]*inv[m]%mod*inv[n-m]%mod;} void init()
{
l=;while ((<<l)<=_n) l++;//1<<l必须要大于原来的n
_n=<<l; int ww=ksm(root,<<-l);
w[]=;
for (int i=;i<=_n;i++)
w[i]=(ll)ww*w[i-]%mod,pos[i]=((i&)<<l-)|(pos[i>>]>>);
}
void fft(int *a)
{
for (int i=;i<_n;i++) if (pos[i]<i) swap(a[i],a[pos[i]]);//swap可以避免多用一个数组和慢得要死的memset
int len=,id=_n;
for (int i=;i<l;i++)
{
int g=w[id>>=];
for (int j=;j<_n;j+=*len)
for (int k=,ww=;k<len;k++)
{
int L=a[j+k],R=(ll)a[j+len+k]*ww%mod;
a[j+k]=((ll)L+R)%mod;a[j+len+k]=((ll)L-R+mod)%mod;
ww=(ll)ww*g%mod;
}
len<<=;
}
}
int main()
{
n=read();
jc[]=inv[]=jc[]=inv[]=;
for (int i=;i<=;i++) jc[i]=(ll)jc[i-]*i%mod,inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
for (int i=;i<=;i++) inv[i]=(ll)inv[i-]*inv[i]%mod;
for (int i=;i<=n;i++)
{
a=read(),b=read();sum+=a;
q.insert(P(len[i]=min(a,b)+,i));
for (int j=;j<=min(a,b);j++)
vec[i].push_back((ll)c(a,j)*c(b,j)%mod*jc[j]%mod);
}
for (int i=;i<n;i++)
{
k1=q.begin()->sec;q.erase(q.begin());
k2=q.begin()->sec;q.erase(q.begin());
memcpy(f,vec[k1].data(),len[k1]*);
memcpy(g,vec[k2].data(),len[k2]*);
vec[k1].clear();vec[k2].clear(); _n=len[k1]+len[k2]-;
init();fft(f);fft(g);
for (int j=;j<_n;j++) f[j]=(ll)f[j]*g[j]%mod;
fft(f);reverse(f+,f+_n); int inv_n=ksm(_n,mod-);
for (int j=;j<_n;j++) vec[k1].push_back((ll)f[j]*inv_n%mod),f[j]=g[j]=;
q.insert(P(len[k1]=_n,k1));
}
int u=q.begin()->sec;
for (int i=;i<=sum;i++)
{
int x=(ll)vec[u][i]*jc[sum-i]%mod;
if (i&) ans=((ll)ans-x+mod)%mod;else ans=((ll)ans+x)%mod;
}
printf("%d\n",ans);
return ;
}

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序。要在块端点子树内才会被该端点管辖。

标程:

 #include<bits/stdc++.h>
using namespace std;
int read()
{
int x=,f=;char ch=getchar();
while (ch<''||ch>'') {if (ch=='-') f=-;ch=getchar();}
while (ch>=''&&ch<='') x=(x<<)+(x<<)+ch-'',ch=getchar();
return x*f;
}
const int N=;
vector<int> vec_a[N],vec_b[N];
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;
struct node{int x,y,sz,bf;}sta[N*];
int find(int x){return x==f[x]?x:find(f[x]);}
void merge(int x,int y)
{
x=find(x);y=find(y);
if (x==y) return;
if (sz[x]>sz[y]) swap(x,y);
sta[++top].x=x;sta[top].y=y;sta[top].sz=sz[y];sta[top].bf=f[x];
f[x]=y;if (sz[x]==sz[y]) sz[y]++;
}
void Re(int x)
{
while (top>x)
{
f[sta[top].x]=sta[top].bf;
sz[sta[top].y]=sta[top].sz;
top--;
}
}
void work(int x)
{
int t=top;
for (int i=x;i!=tt;i=Fa[i]) merge(au[i],av[i]);
ans[x]=m-top;vis[x]=;
Re(t);
}
void dfs_B(int x,int fa)
{
int t=top;merge(bu[x],bv[x]);
if (!vis[x]&&dfn[x]>=dfn[tt]) work(x);
for (int i=;i<vec_b[x].size();i++)
if (vec_b[x][i]!=fa) dfs_B(vec_b[x][i],x);
Re(t);
}
void dfs_A(int x,int fa)
{
int t=top; merge(au[x],av[x]);
dep[x]=;dfn[x]=++Dfn;
for (int i=;i<vec_a[x].size();i++)
if (vec_a[x][i]!=fa)
{
dfs_A(vec_a[x][i],x);
dep[x]=max(dep[x],dep[vec_a[x][i]]+);
}
if (dep[x]==||x==) tt=x,dfs_B(,-),dep[x]=;
Re(t);
}
int main()
{
int T=read();
while (T--)
{
n=read();m=read();Dfn=top=;
for (int i=;i<=n;i++) vec_a[i].clear(),vec_b[i].clear(),vis[i]=dfn[i]=;
for (int i=;i<=n;i++) au[i]=read(),av[i]=read();
for (int i=;i<n;i++) u=read(),v=read(),vec_a[u].push_back(v),vec_a[v].push_back(u),Fa[v]=u;
for (int i=;i<=n;i++) bu[i]=read(),bv[i]=read();
for (int i=;i<n;i++) u=read(),v=read(),vec_b[u].push_back(v),vec_b[v].push_back(u);
for (int i=;i<=m;i++) f[i]=i,sz[i]=;
dfs_A(,-);
for (int i=;i<=n;i++) assert(vis[i]==);
for (int i=;i<=n;i++) printf("%d\n",ans[i]);
}
return ;
}

soj考试2的更多相关文章

  1. [SOJ #721]第三送分题(2019-11-14考试)/[CF675E]Trains and Statistic

    题目大意 在一条直线上有\(n\)个点.在第\(i\)个点可以花费\(1\)的代价到达\((i,a_i]\)中任意一点,用\(S[i][j]\)表示从点\(i\)到点\(j\)的最少花费,求\(\su ...

  2. [SOJ #696]染色(2019-11-10考试)/[Atcoder MUJIN Programming Challenge C]Orange Graph

    题目大意 有一个\(n\)个点\(m\)条边的简单无向连通图,初始为白色,可以执行操作让一些边变黑,要求使得操作后的图不存在黑色的奇环,且不能使得其他的任何变黑而还符合要求.问最后有多少可能结果.\( ...

  3. [SOJ #687]双生串(2019-11-6考试)/[hdu5431]AB String

    题目大意 把所有仅包含\(AB\)的字符串按字典序排列,给你一个仅包含\(AB\)的字符串\(S\),然后有\(Q\)个问题,第\(i\)个问题给你\(k_i\),求不是\(S\)的子串中,第\(k_ ...

  4. [SOJ #686]抢救(2019-11-7考试)/[洛谷P3625][APIO2009]采油区域

    题目大意 有一个\(n\times m\)的网格,\((x,y)\)权值为\(a_{x,y}\),要求从中选取三个不相交的\(k\times k\)的正方形使得它们权值最大.\(n,m,k\leqsl ...

  5. [SOJ #498]隔膜(2019-10-30考试)/[POJ2152]Fire

    题目大意:有一棵$n$个点的带边权树,第$i$个点有两个值$w_i,d_i$,表示在这个点做标记的代价为$w_i$,且这个点距离$d_i$以内至少要有一个点被标记,为最小代价.$n\leqslant6 ...

  6. [SOJ #537]不包含 [CF102129I]Incomparable Pairs(2019-8-6考试)

    题目大意:给定一个长度为$n$的字符串$s$,求有多少个无序字符串二元组$(x,y)$满足:$x,y$是$s$的字串,且$x$不是$y$的字串,$y$不是$x$的字串 题解:发现满足$x,y$是$s$ ...

  7. [SOJ #538]好数 [CC]FAVNUM(2019-8-6考试)

    题目大意:给定$n$个正整数,求$[l,r]$中第$k$小的”好数“.$l,r\leqslant10^{18},n\leqslant62$,出现的其他数均$\leqslant10^{50}$ 好数定义 ...

  8. 全网独家MongoDB Certified DBA Associate考试认证视频

    该视频意在让所有学员一次通过考试,避免重复考试而承担的巨额考试费用! 目前MongDB发展迅猛,有赶超mysql,和oracle看齐的苗头.在这个时候MongoDB也适时的推出了官方的认证考试&quo ...

  9. 记lrd的高二上学期第五次调研考试

    河北某某中学的调研考试其实是很好玩的经历呢.可惜没有太多机会了. 背景: NOIP2016回来之后没有好好学文化课-.自习能翘就翘了,衡中特产学案自助没有好好写(说来我好像从来没被老师查到过,上课写学 ...

随机推荐

  1. JUC源码分析-线程池篇(三)Timer

    JUC源码分析-线程池篇(三)Timer Timer 是 java.util 包提供的一个定时任务调度器,在主线程之外起一个单独的线程执行指定的计划任务,可以指定执行一次或者反复执行多次. 1. Ti ...

  2. 安装jdk 并放在 /usr/java/default 目录下

    安装步骤 . mkdir /usr/java 2. tar -xvf jdk*.tar.gz -C /usr/java/ 记得将* 换成版本即可3. ln -s /usr/java/jdk* /usr ...

  3. 第六篇 xpath的用法

    使用pycharm debug调试效率会比较慢,因为每次调试都需要向url发送请求,等返回信息,scrapy提供一种方便调试的功能,如下: >>>(third_project) bi ...

  4. json传参报错

    restful接口报错: com.fasterxml.jackson.core.JsonParseException: Unexpected character ('e' (code 101)): w ...

  5. 初探Javascript魅力(1)

    转自:CSDN--http://blog.csdn.net/cherry_vicent/article/details/42120149 1.javascript是什么   根据用户的一些操作,然后来 ...

  6. position:relative/static/fixed/absolute定位的区别以及使用场景

    absolute是相对于自己最近的父元素来定位的,relative是相对于自己来定位的 relative 不脱离文档流,absolute 脱离文档流.也就是说:relative 的元素尽管表面上看到它 ...

  7. 微信1.8.6.1 SDK 无法授权登录解决办法

    我用的cocos2d-lua 3.9 项目打包 调用微信授权登录的时候 授权登录接口一直抛异常导致微信都无法拉起来 按照官网配置了universal link (这个也搞了很长时间jason 配置文件 ...

  8. 安装Storm的基本过程

  9. Jenkins 搭建 .NET FrameWork 持续集成环境

    本文不赘述如何安装 Jenkins,如有需要请看之前文章,这里我们主要搭建 .Net 环境.本文是在 Windows 环境下安装的 Jenkins 进行操作     一.安装所需环境     这里我们 ...

  10. 用Cygwin实现在window环境下使用Linux命令-nohup 来后台运行程序

    1.安装Cygwin 下载 cygdrive-选择64或32位   http://www.cygwin.com/ 注:可以百度搜索安装步骤 2.配置它的环境变量 添加到path路径中 3.cmd  执 ...