8.8考试总结(NOIP模拟33)[Hunter·Defence·Connect]

无法逃避的是自我,而无法挽回的是过去。

前言

还算可以,不过 T1 少 \(\bmod\) 了一下挂了 25pts,T2 没看清题面挂了 27pts。

下回注意吧。。

T1 Hunter

解题思路

感觉正解不是很好想到,但是看题解就比较好看懂。。

1 号猎人死亡的轮数等于在 1 号之前死亡的猎人数 +1。

根据期望的 线性 性, 就等于每个猎人比 1 号猎人先死的概率和。

不难发现第 i 个猎人比 1 号猎人先死的概率是 \(\dfrac{W_{i}}{W_{1}+W_{i}}\)

上面的内容直接从官方题解摘过来了,实现没有任何难度。。

说一下记忆化搜索吧。。。

很明显 对于 \(n\le 10\) 的点一般的搜索就可以了。

对于 \(n\le 20\)的就需要记忆化了。

记忆化向下搜索的时候最好不要传一些概率那一类的东西,比较难把控。

对于下一层的概率最好在这一层就算好。。。

然后再特判一下 1 的情况就好了。

code

25pts DFS

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=1e5+10,M=1e6+10,mod=998244353;
int n,ans,inv[M],s[N],f[1<<21];
int ksm(int x,int y)
{
	int tmp=1;
	while(y)
	{
		if(y&1)	tmp=tmp*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return tmp%mod;
}
void dfs(int sta,int round,int E)
{
	if(!(sta&1))	return ;
	ans=(ans+f[sta]*s[1]%mod*round%mod*E%mod)%mod;
	for(int i=1;i<=n;i++)
		if((sta>>i-1)&1)
			dfs(sta^(1<<i-1),round+1,E*f[sta]%mod*s[i]%mod);
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
		s[i]=read();
	for(int i=1;i<(1<<n);i++)
	{
		int sum=0;
		for(int j=1;j<=n;j++)
			if((i>>j-1)&1)
				sum=(sum+s[j])%mod;
		f[i]=ksm(sum,mod-2);
	}
	dfs((1<<n)-1,1,1);
	printf("%lld",ans);
	return 0;
}

45pts 记忆化 DFS

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=1e5+10,M=1e6+10,mod=998244353;
int n,ans,inv[M],s[N],f[1<<21],dp[1<<21];
int ksm(int x,int y)
{
	int tmp=1;
	while(y)
	{
		if(y&1)	tmp=tmp*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return tmp%mod;
}
int dfs(int sta,int round)
{
	if(!(sta&1))	return 0;
	if(dp[sta])	return dp[sta];
	dp[sta]=(dp[sta]+f[sta]*s[1]%mod*round%mod)%mod;
	for(int i=1;i<=n;i++)
		if((sta>>i-1)&1)
			dp[sta]=(dp[sta]+dfs(sta^(1<<i-1),round+1)*f[sta]%mod*s[i]%mod)%mod;
	return dp[sta];
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
		s[i]=read();
	for(int i=1;i<(1<<n);i++)
	{
		int sum=0;
		for(int j=1;j<=n;j++)
			if((i>>j-1)&1)
				sum=(sum+s[j])%mod;
		f[i]=ksm(sum%mod,mod-2);
	}
	printf("%lld",dfs((1<<n)-1,1));
	return 0;
}

正解

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int mod=998244353,N=1e5+10;
int n,ans,s[N];
int ksm(int x,int y)
{
	int tmp=1;
	while(y)
	{
		if(y&1)	tmp=tmp*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return tmp%mod;
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
		s[i]=read();
	for(int i=2;i<=n;i++)
		ans=(ans+s[i]*ksm((s[i]+s[1]%mod),mod-2)%mod)%mod;
	printf("%lld",(ans+1)%mod);
	return 0;
}

T2 Defence

解题思路

首先感谢出题人没有写太多一个节点有多个法术的测试点。

其次,抱怨一下题目描述不清楚。。。

非常像之前做过的玫瑰花精这道题。

主要维护 7 个值,(其实有一些没有用的,但也无伤大雅)。

最左边的 1 的坐标以及对应的最左侧的连续的 0 的长度。

同样的东西,在右边也维护一个类似的。

还有一个中间的最大区间的长度以及左右端点。

也就是下图所表示的。

剩下的比较难写的就是 push_up 函数了,别的还好。

然后就是线段树合并一系列的事情了。。。。

code

AC代码

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
#define ls tre[x].l
#define rs tre[x].r
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=1e5+10;
int n,m,q,ans[N],root[N],fa[N];
int tot=1,head[N],nxt[N],ver[N];
int all;
vector<int > p[N];
struct Segment_Tree
{
	int l,r,lp,rp,lmx,rmx,llen,rlen,len;
}tre[N*80];
void push_up(int x,int l,int r)
{
	if(!ls&&!rs)	return ;
	if(ls)
	{
		tre[x].lp=tre[ls].lp;
		tre[x].llen=tre[ls].lp-l;
	}
	else
	{
		tre[x].lp=tre[rs].lp;
		tre[x].llen=tre[rs].lp-l;
	}
	if(rs)
	{
		tre[x].rp=tre[rs].rp;
		tre[x].rlen=r-tre[rs].rp;
	}
	else
	{
		tre[x].rp=tre[ls].rp;
		tre[x].rlen=r-tre[ls].rp;
	}
	int len1=tre[ls].len,len2=tre[rs].len,len3=0;
	if(ls&&rs)	len3=tre[ls].rlen+tre[rs].llen;
	if(len1>=max(len2,len3))
	{
		tre[x].len=len1;
		tre[x].lmx=tre[ls].lmx;
		tre[x].rmx=tre[ls].rmx;
	}
	else	if(len2>=max(len1,len3))
	{
		tre[x].len=len2;
		tre[x].lmx=tre[rs].lmx;
		tre[x].rmx=tre[rs].rmx;
	}
	else
	{
		tre[x].len=len3;
		tre[x].lmx=tre[ls].rp;
		tre[x].rmx=tre[rs].lp;
	}
}	
void insert(int &x,int l,int r,int pos)
{
	if(!x)	x=++all;
	if(l==r)
	{
		tre[x].lp=tre[x].rp=tre[x].lmx=tre[x].rmx=l;
		return ;
	}
	int mid=(l+r)>>1;
	if(pos<=mid)	insert(ls,l,mid,pos);
	else	insert(rs,mid+1,r,pos);
	push_up(x,l,r);
}
int merge(int x,int y,int l,int r)
{
	if(!x||!y)	return x+y;
	if(l==r)	return x;
	int mid=(l+r)>>1;
	ls=merge(ls,tre[y].l,l,mid);
	rs=merge(rs,tre[y].r,mid+1,r);
	push_up(x,l,r);
	return x;
}
void add(int x,int y)
{
	ver[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}
void dfs(int x)
{
	if(p[x].size())
		for(int i=0;i<p[x].size();i++)	
			insert(root[x],1,m,p[x][i]);
	for(int i=head[x];i;i=nxt[i])
	{
		int to=ver[i];
		dfs(to);
		root[x]=merge(root[x],root[to],1,m);
	}
	ans[x]=max(tre[root[x]].len,tre[root[x]].llen+tre[root[x]].rlen);
}
signed main()
{
	n=read();
	m=read();
	q=read();
	for(int i=1,x,y;i<n;i++)
	{
		x=read();
		y=read();
		add(x,y);
		fa[y]=x;
	}
	for(int i=1,x,y;i<=q;i++)
	{
		x=read();
		y=read();
		p[x].push_back(y);
	}
	dfs(1);
	for(int i=1;i<=n;i++)
	{
		if(!root[i])	printf("-1\n");
		else	printf("%lld\n",ans[i]);
	}
	return 0;
}

造数据必备

可以出来链的,菊花图的,满二叉树的。。

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
int random(int l,int r)
{
	return rand()%(r-l+1)+l;
}
signed main()
{
	freopen("date.in","w",stdout);
	srand(time(0));
	int n=10,m=20,q=random(1,10),opt=random(1,3);
	cout<<n<<' '<<m<<' '<<q<<endl;
	if(opt==1)	for(int i=2;i<=n;i++)	cout<<(i+1)/2<<' '<<i<<endl;
	else	if(opt==2)	for(int i=2;i<=n;i++)	cout<<1<<' '<<i<<endl;
	else	for(int i=2;i<=n;i++)	cout<<i-1<<' '<<i<<endl;
	for(int i=1;i<=q;i++)	cout<<random(1,n)<<' '<<random(1,m)<<endl;
	return 0;
}

T3 Connect

解题思路

考场上想的是先跑一个最大生成树,然后对于 n 所在的子树上进行一些操作。

后来尽管算了一下时间复杂度小的离谱,但还是交了上去QAQ。

然后考完之后看了一眼题解:状压,这复杂度才对嘛。

后来下午讲题的时候 战神 拿出了他的 AC_Code。

然后瞥了一眼四周都在截图。。。(我也。。

\(f_{sta,i}\) 表示现在状态是 sta ,这个联通块(或者说是挂了一些东西的链)的结尾是 j 节点。

然后状态的转移就可以是,在这个链上再挂上一些联通块,或者在链的尾部新加入节点。

预处理出每一种联通块内部的边的总价值。

以及某一个联通块向联通块外面的某一个点的连边的总价值。

最后进行状压转移就好了。。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=16;
int n,m,cet[1<<N][N],edge[N][N],all[1<<N],f[1<<N][N];
signed main()
{
	n=read();m=read();
	for(int i=1,x,y,val;i<=m;i++)
	{
		x=read();y=read();val=read();
		edge[x][y]=edge[y][x]=val;
	}
	for(int i=1;i<(1<<n);i++)
		for(int j=1;j<=n;j++)
			for(int k=j+1;k<=n;k++)
				if((i>>j-1)&1 and (i>>k-1)&1)
					all[i]+=edge[j][k];
	for(int i=1;i<(1<<n);i++)
		for(int j=1;j<=n;j++)
			for(int k=1;k<=n;k++)
				if((i>>j-1)&1 and !((i>>k-1)&1))
					cet[i][k]+=edge[j][k];
	memset(f,-1,sizeof(f));
	f[1][1]=0;
	for(int i=1;i<(1<<n);i++)
		for(int j=1;j<=n;j++)
			if(f[i][j]!=-1)
			{
				for(int k=1;k<=n;k++)
					if(!((i>>k-1)&1) and edge[j][k])
						f[i|(1<<k-1)][k]=max(f[i|(1<<k-1)][k],f[i][j]+edge[j][k]);
				for(int k=((1<<n)-1)^i;k;k=(((1<<n)-1)^i)&(k-1))
					f[i|k][j]=max(f[i|k][j],f[i][j]+all[k]+cet[k][j]);
			}
	printf("%lld",all[(1<<n)-1]-f[(1<<n)-1][n]);
	return 0;
}
posted @ 2021-08-08 17:34  Varuxn  阅读(107)  评论(0编辑  收藏  举报