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. DevOps到底是什么鬼?DevOps介绍及工具推荐。

    什么是DevOps DevOps是Development和Operations的组合,是一组过程.方法与系统的统称,用于促进开发(应用程序/软件工程).技术运营和质量保障(QA)部门之间的沟通.协作与 ...

  2. 4-Ubuntu-启动/关闭/重启mysql服务

    启动: sudo service mysql start 关闭: sudo service mysql stop 重启: sudo service mysql restart

  3. 2018湘潭大学程序设计竞赛【A】

    题目链接:https://www.nowcoder.com/acm/contest/105/A 题意:给你起始和结束的天时分,让你算总秒数. 题解:输入格式.注意long long.签到题. #inc ...

  4. shell脚本将命令的结果赋值给变量的2种写法

    Shell 也支持将命令的执行结果赋值给变量,常见的有以下两种方式: variable=`command`variable=$(command) 第一种方式把命令用反引号` `(位于 Esc 键的下方 ...

  5. Batch - 忽略FORFILES “no files found” error

    ref:https://stackoverflow.com/questions/16820681/suppress-forfiles-no-files-found-error Solution: Th ...

  6. delphi xe10 麦克风、摄像头操作

    TakePhotoFromCameraAction1: TTakePhotoFromCameraAction; // 通过手机摄像头获取图片TakePhotoFromLibraryAction1: T ...

  7. Perl 运算符

    Perl 运算符 运算符是一种告诉编译器执行特定的数学或逻辑操作的符号,如: 3+2=5. Perl 语言内置了丰富的运算符,我们来看下常用的几种: 算术运算符 比较运算符 逻辑运算符 赋值运算符 位 ...

  8. jQuery 事件 click() 方法,dblclick() 方法

    click() 方法 当点击元素时,会发生 click 事件. 当鼠标指针停留在元素上方,然后按下并松开鼠标左键时,就会发生一次 click. click() 方法触发 click 事件,或规定当发生 ...

  9. 40 VSCode下.json文件的编写——(1) linux/g++ (2).json中参数与预定义变量的意义解释

    0 引言 转入linux/VSCode编程之后,迫切了解到有必有较为系统地学习一下VSCode中相关配置文件的写法.下面将分为 linux/g++编译指令..json文件关键词/替换变量的意义.编译链 ...

  10. LUOGU P4560 [IOI2014]Wall 砖墙 (线段树)

    传送门 解题思路 线段树打标记,刚开始想复杂了,维护了四个标记.后来才知道只需要维护一个最大值最小值即可,然后更新的时候分类讨论一下. 代码 #include<iostream> #inc ...