zoukankan      html  css  js  c++  java
  • 18.11.5绍一模拟赛

    T1树

    题意

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

    分析

    我们想要把一棵树染成白色,发现如果我们染了根节点,次数就会加一。
    如果没有染根节点,根节点会自动被染好。
    那么染好一棵树的期望次数就是把它左右子树染好的期望次数+染了根节点从期望次数。
    (f[i]=sum_k^{kin 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
    ",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
    ",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
    ",f[1]);
    	return 0;
    }
    

    T2图

    题意

    求一张图(nle 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
    ",f[1<<n][n+1]);
    	return 0;
    }
    

    T3集合

    题意

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

    分析

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

  • 相关阅读:
    filp_open/filp_close/vfs_read/vfs_write
    memcpy一种实现方法
    memset函数的实现&printf函数几种输出格式的输出结果
    break退出循环分析
    定义指针变量作为返回值函数执行时报 段错误(核心已转储)
    node实现防盗链
    js实现输入密码之延迟星号和点击按钮显示或隐藏
    rem适配
    使用字蛛教程以及遇到的bug
    es6学习笔记-proxy对象
  • 原文地址:https://www.cnblogs.com/onglublog/p/9909288.html
Copyright © 2011-2022 走看看