18.11.5绍一模拟赛

T1树

题意

给定一棵\(n(n\le 10^7)\)个点,一开始点全是黑的树,每次随机选一个黑点,把从他到根节点的路径上的点全部变成白色。
求把树全部染白的期望染色次数模\(998244353\)意义下的结果。

分析

我们想要把一棵树染成白色,发现如果我们染了根节点,次数就会加一。
如果没有染根节点,根节点会自动被染好。
那么染好一棵树的期望次数就是把它左右子树染好的期望次数+染了根节点从期望次数。
\(f[i]=\sum_k^{k\in child[i]}{f[k]+}\frac{1}{siz[i]}\)
然后打了个记搜爆了。。。。
然后打了个从叶子递推爆了。。。。
然而并不理解为什么子节点的序号一定比父节点大。
直接从\(n\)循环到\(1\)就过了。。

代码

记搜

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#define ll long long
#define file "tree"
using namespace std;
const int N=10000009;
const int mod=998244353;
struct Node{
	int siz;
	vector<int>son;
}tree[N];
int read(){
	char c;int num,f=1;
	while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0';
	while(c=getchar(), isdigit(c))num=num*10+c-'0';
	return f*num;
}
int Pow(int a,int p){
	int ans=1;
	for(;p;p>>=1,a=(1ll*a*a)%mod)
		if(p&1)ans=1ll*a*ans%mod;
	//cout<<ans<<endl;
	return ans;
}
int inv(int x){
	return Pow(x,mod-2);
}
void dfs(int x){
	for(int i=0;i<tree[x].son.size();i++){
		dfs(tree[x].son[i]);
		tree[x].siz+=tree[tree[x].son[i]].siz;
	}
	tree[x].siz+=1;
}
int n,m,f[N];
int get(int x){
	if(f[x])return f[x];
	int tot=tree[x].siz,k=inv(tot),tmp=0;
	for(int i=0;i<tree[x].son.size();i++){
		tmp+=get(tree[x].son[i]);
		if(tmp>=mod)tmp-=mod;
	}
	f[x]=tmp+k;
	if(f[x]>=mod)f[x]-=mod;
	return f[x];
}
int main()
{
	freopen(file".in","r",stdin);
	freopen(file".out","w",stdout);
	n=read();
	for(int i=1;i<n;i++)
		tree[read()].son.push_back(i+1);
	dfs(1);
	printf("%d\n",get(1));
	return 0;
}

从叶子递推

#include <bits/stdc++.h>
using namespace std;
const int N=10000009;
const int mod=998244353;
int read(){
	char c;int num,f=1;
	while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0';
	while(c=getchar(), isdigit(c))num=num*10+c-'0';
	return f*num;
}
int n,fa[N],ch[N],f[N];
int siz[N],inv[N];
bool leaf[N];
void add(int &a,int b){
	a+=b;
	if(a>=mod)a-=mod;
}
int Pow(int a,int p){
	int ans=1;
	for(;p;p>>=1,a=1ll*a*a%mod)
		if(p&1)ans=1ll*ans*a%mod;
	return ans;
}
void init(){
	inv[1]=1;
	for(int i=2;i<=10000002;i++)
		inv[i]=(1ll*mod-mod/i)*inv[mod%i]%mod;
}
void push_up(int x){
	int k;
	while(x&&ch[x]==0){
		add(siz[x],1);
		add(siz[fa[x]],siz[x]);
		//累加到父节点 
		ch[fa[x]]--;
		//父节点儿子统计 
		k=inv[siz[x]];
		add(f[x],k);
		//计算当前期望
		add(f[fa[x]],f[x]);
		//累加父亲期望
		x=fa[x];
		//继续查找父亲 
	}
}
int main()
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	n=read();init();
	for(int i=2;i<=n;i++){
		fa[i]=read();
		ch[fa[i]]++;
		leaf[fa[i]]=1;
	}
	for(int i=1;i<=n;i++){
		if(!leaf[i])push_up(i);
	}
	printf("%d\n",f[1]);
	return 0;
}

循环

#include <bits/stdc++.h>
using namespace std;
const int N=10000009;
const int mod=998244353;
int read(){
	char c;int num,f=1;
	while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0';
	while(c=getchar(), isdigit(c))num=num*10+c-'0';
	return f*num;
}
int n,fa[N],ch[N],f[N];
int siz[N],inv[N];
bool leaf[N];
void add(int &a,int b){
	a+=b;
	if(a>=mod)a-=mod;
}
int Pow(int a,int p){
	int ans=1;
	for(;p;p>>=1,a=1ll*a*a%mod)
		if(p&1)ans=1ll*ans*a%mod;
	return ans;
}
void init(){
	inv[1]=1;
	for(int i=2;i<=10000002;i++)
		inv[i]=(1ll*mod-mod/i)*inv[mod%i]%mod;
}
int main()
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	n=read();init();
	for(int i=2;i<=n;i++)fa[i]=read();
	for(int i=n;i>=1;i--){
		siz[i]++;
		siz[fa[i]]+=siz[i];
		add(f[i],inv[siz[i]]);
		add(f[fa[i]],f[i]);
	}
	printf("%d\n",f[1]);
	return 0;
}

T2图

题意

求一张图\(n\le 18\)\(dfs\)序数量

分析

发现\(n\)很小。应该是个爆搜或者状压。
我的写法是记搜+状压。
可以从任意一点开始深搜,那么我们可以新建一个虚拟节点\(n+1\)号点,然后钦定\(n+1\)为第一个遍历的点,然后进行记搜。
\(g[s][i]\)表示已遍历的节点状态\(s\),现在从\(i\)点开始跑,能抵达的节点的状态(包括\(i\)但不包括以前的节点)。
\(f[s][i]\)表示已经遍历\(s\)状态,现在从\(i\)节点开始遍历,以\(i\)为根的搜索树的数量。

\(i\)号点开始寻找出点,我们考虑出点之间的关系。
如果两个出点在删除掉已经遍历的点之后仍然联通,那么这两个节点肯定互相影响,也就是说对一个点进行深搜,回溯的时候不能搜另外一个点。
所以说这两个点的dfs序数量满足加法原理。
如果两个点在删除掉已经遍历的点之后不连通,那么这两个节点没有任何关系,换句话说,不论一个点的遍历顺序怎么样,另外一个点的遍历顺序都可以随便取。
那么这两个点满足乘法原理。
我们对一个残图的一个点进行深搜,搜索出每个出边能遍历到的点,搜索出若干个点集,每个点集相互独立,点集内的\(dfs\)序也是独立的。
我们只要把点集的\(dfs\)序全部相乘,然后再乘上这些点集的排列数,就是当前状态了。

代码

删掉注释70行不到,感觉还是不长的。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define ll long long
#define file "graph"
using namespace std;
const int mod=998244353;
const int N=20,M=N*(N-1)*2;
int read(){
	char c;int num,f=1;
	while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0';
	while(c=getchar(), isdigit(c))num=num*10+c-'0';
	return f*num;
}
int n,m,vis[N],ans=0,f[(1<<N)+1][N];
int head[N],nxt[M],ver[M],tot=1,g[(1<<N)+1][N];
void add(int u,int v){
	ver[++tot]=v;nxt[tot]=head[u];head[u]=tot;
	ver[++tot]=u;nxt[tot]=head[v];head[v]=tot;
}
void print(int x){
	while(x){
		cout<<(x&1);
		x>>=1;
	}
}
int dfs1(int s,int x){
	if(g[s][x])return g[s][x];
	g[s][x]|=(1<<x-1);
	//if(x==2)cout<<s<<endl;
	for(int i=head[x];i;i=nxt[i]){
		int y=ver[i];
		//if(x==2&&y==1)cout<<(s&(1<<y-1))<<endl;
		if(s&(1<<y-1))continue;
		dfs1(s|((1<<y-1)),y);
		g[s][x]|=g[s|((1<<y-1))][y];	
	}
	return g[s][x];
}
//以当前点为根节点的搜索树的状态 
int dfs(int s,int u){
	if(f[s][u])return f[s][u];
	f[s][u]=1;
	if(!head[u])return f[s][u];
	int gg[20][4],cnt=0,k,flag=0;
	memset(gg,0,sizeof(gg));
	//print(s);cout<<"  "<<u<<endl;
	
	for(int i=head[u];i;i=nxt[i]){
		int y=ver[i];flag=0;
		if(s&(1<<y-1))continue;
		k=dfs1(s|(1<<y-1),y);
		//k是状态
		//if(u==4&&s==8&&k==6)cout<<y<<endl;
		
		for(int j=1;j<=cnt;j++){
			if(gg[j][2]==k){
				flag=1;gg[j][1]+=dfs(s|(1<<y-1),y);
				if(gg[j][1]>=mod)gg[j][1]-=mod;
				break;
			}
		}
		//遍历原来的状态 
		if(!flag){
			gg[++cnt][1]=dfs(s|(1<<y-1),y);
			gg[cnt][2]=k;
		}
		//新开状态 
	}
	/*if(u==4&&s==8){
		cout<<gg[1][2]<<endl;
	}*/
	for(int i=1;i<=cnt;i++)
		f[s][u]=1ll*f[s][u]*i%mod*gg[i][1]%mod;
	return f[s][u];
}
int main()
{
	freopen(file".in","r",stdin);
	freopen(file".out","w",stdout);
	n=read();m=read();
	for(int i=1;i<=m;i++){
		int u=read(),v=read();
		add(u,v);	
	}
	for(int i=1;i<=n;i++){
		add(n+1,i);
	}
	dfs((1<<n),n+1);
	//cout<<g[12][3]<<endl;
	printf("%d\n",f[1<<n][n+1]);
	return 0;
}

T3集合

题意

给定\(n,m(n,m\le 2000)\),构造两个没有交集的集合,要求\(A\)集合里的数小于\(n\)\(B\)集合里的数小于\(m\)
并且\(A\)集合的异或和小于\(B\)集合。
求构造的方案数。

分析

不会写啊啊啊啊啊啊!!!!
(逃了)

posted @ 2018-11-05 15:34  _onglu  阅读(112)  评论(0编辑  收藏  举报