soj考试2
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的更多相关文章
- [SOJ #721]第三送分题(2019-11-14考试)/[CF675E]Trains and Statistic
题目大意 在一条直线上有\(n\)个点.在第\(i\)个点可以花费\(1\)的代价到达\((i,a_i]\)中任意一点,用\(S[i][j]\)表示从点\(i\)到点\(j\)的最少花费,求\(\su ...
- [SOJ #696]染色(2019-11-10考试)/[Atcoder MUJIN Programming Challenge C]Orange Graph
题目大意 有一个\(n\)个点\(m\)条边的简单无向连通图,初始为白色,可以执行操作让一些边变黑,要求使得操作后的图不存在黑色的奇环,且不能使得其他的任何变黑而还符合要求.问最后有多少可能结果.\( ...
- [SOJ #687]双生串(2019-11-6考试)/[hdu5431]AB String
题目大意 把所有仅包含\(AB\)的字符串按字典序排列,给你一个仅包含\(AB\)的字符串\(S\),然后有\(Q\)个问题,第\(i\)个问题给你\(k_i\),求不是\(S\)的子串中,第\(k_ ...
- [SOJ #686]抢救(2019-11-7考试)/[洛谷P3625][APIO2009]采油区域
题目大意 有一个\(n\times m\)的网格,\((x,y)\)权值为\(a_{x,y}\),要求从中选取三个不相交的\(k\times k\)的正方形使得它们权值最大.\(n,m,k\leqsl ...
- [SOJ #498]隔膜(2019-10-30考试)/[POJ2152]Fire
题目大意:有一棵$n$个点的带边权树,第$i$个点有两个值$w_i,d_i$,表示在这个点做标记的代价为$w_i$,且这个点距离$d_i$以内至少要有一个点被标记,为最小代价.$n\leqslant6 ...
- [SOJ #537]不包含 [CF102129I]Incomparable Pairs(2019-8-6考试)
题目大意:给定一个长度为$n$的字符串$s$,求有多少个无序字符串二元组$(x,y)$满足:$x,y$是$s$的字串,且$x$不是$y$的字串,$y$不是$x$的字串 题解:发现满足$x,y$是$s$ ...
- [SOJ #538]好数 [CC]FAVNUM(2019-8-6考试)
题目大意:给定$n$个正整数,求$[l,r]$中第$k$小的”好数“.$l,r\leqslant10^{18},n\leqslant62$,出现的其他数均$\leqslant10^{50}$ 好数定义 ...
- 全网独家MongoDB Certified DBA Associate考试认证视频
该视频意在让所有学员一次通过考试,避免重复考试而承担的巨额考试费用! 目前MongDB发展迅猛,有赶超mysql,和oracle看齐的苗头.在这个时候MongoDB也适时的推出了官方的认证考试&quo ...
- 记lrd的高二上学期第五次调研考试
河北某某中学的调研考试其实是很好玩的经历呢.可惜没有太多机会了. 背景: NOIP2016回来之后没有好好学文化课-.自习能翘就翘了,衡中特产学案自助没有好好写(说来我好像从来没被老师查到过,上课写学 ...
随机推荐
- 从零开始 Code Review,两年实战经验分享!
作者:wenhx http://www.cnblogs.com/wenhx/p/5641766.html 前几天看了<Code Review 程序员的寄望与哀伤>,想到我们团队开展 Cod ...
- 豆瓣图书Top250
从豆瓣图书Top250抓取数据,并通过词云图展示 导入库 from lxml import etree #解析库 import time #时间 import random #随机函数 import ...
- vim的基本快捷操作(一)
一.光标移动 ^ 到该行第一个非空格字符处. + 到下一行的第一个非空格字符处 - 到上一行的第一个非空格字符处 `. 到上次修改点 <c-o> 到上次所停留位置, <c-i> ...
- 将Java和Javac的命令在控制台的输出重定向到txt文件
当我们在Windows控制台窗口执行程序时,输入如下命令: demo.exe > out.txt 就可以把demo程序的输出重定向到out.txt文件里面. 但是这种方法对于java和javac ...
- PHP72w安装
PHP72w # rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm # rpm ...
- could not load the tomcat server configuration ...
参考文档: https://blog.csdn.net/u013381651/article/details/51567758 问题解决. 原因是你把项目里的tomcat的server项目也关闭了, ...
- docker一键部署zookeeper
version: '3.1' services: zoo1: image: zookeeper:3.4.11 restart: always hostname: zoo1 container_name ...
- Python自学:第四章 复制列表(1)
# -*- coding: GBK -*- my_foods = ['pizza', 'falafel', 'carrot cake'] friend_foods = my_foods[:] prin ...
- thinkphp 类的扩展
ThinkPHP的类库主要包括公共类库和应用类库,都是基于命名空间进行定义和扩展的.只要按照规范定义,都可以实现自动加载. 大理石平台价格 公共类库 公共类库通常是指ThinkPHP/Library目 ...
- bzoj1217: [HNOI2003]消防局的设立 [树形dp]
Description 2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地.起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了 ...