2021-2022第三届全国大学生算法设计与编程挑战赛(秋季赛) 题解

上午参与了一下,随缘开题,之后会补若干这场感兴趣的题。另外就是最近cf先不跟了,这周之后补补cf,未来可期。

文中代码均为不完全片段,建议配合代码模板食用。

目录

B.二进制

D.分配颜色

E.土地规划 

F.CTF

G.希望

J.抽奖


B.二进制

Solution:

a的每个二进制位对应一棵线段树,开设10棵左右线段树,分别维护区间修改、区间查询即可。

时间复杂度:O(nlogn*loga)

Code:

int n,q;
struct segment
{
	int val,col;
	segment()
	{
		val=col=0;
	}
}tr[20][N<<1];
inline void push_up(int now,int tag)
{
	tr[tag][now].val=tr[tag][now<<1].val+tr[tag][now<<1|1].val;
}
inline void color(int now,int l,int r,int val,int tag)
{
	tr[tag][now].val+=val*(r-l+1);
	tr[tag][now].col+=val;
}
inline void push_down(int now,int l,int r,int tag)
{
	int mid=(l+r)>>1;
	color(lson,tr[tag][now].col,tag);
	color(rson,tr[tag][now].col,tag);
	tr[tag][now].col=0;
}
void upd(int now,int l,int r,int nl,int nr,int val,int tag)
{
	if(nl<=l&&r<=nr)
	{
		color(now,l,r,val,tag);
		return;
	}
	push_down(now,l,r,tag);
	int mid=(l+r)>>1;
	if(nl<=mid)	upd(lson,nl,nr,val,tag);
	if(mid>1;
	int ans=0;
	if(nl<=mid)	ans+=qry(lson,nl,nr,tag);
	if(mid>n>>q;
	while(q--)
	{
		int opt,a,l,r,k;
		cin>>opt>>a>>l>>r;
		if(opt==1)
		{
			cin>>k;
			int tmp=1;
			while(a)
			{
				if(a&1)upd(1,1,n,l,r,k,tmp);
				a>>=1;++tmp;
			}
		}
		else
		{
			int ans=0,tmp=1;
			while(a)
			{
				if(a&1)ans+=qry(1,1,n,l,r,tmp);
				a>>=1;++tmp;
			}
			cout<

D.分配颜色

Solution:

组合计数,分析可知最后蓝色的方格即为被操作过奇数次的位置。

考虑最终有pi行,qi列被操作过奇数次的状态,该状态共有p*m+q*n-2*p*q个蓝色方格,若方格数等于t则可能对答案产生贡献。(pi,qi)状态共有C(n,pi)*C(m,qi)种情况。

易知如果p

因此答案ans=Sigma C(n,pi)*C(m,qi)*C(n+dp/2-1,dp/2)*C(m+dq/2-1,dq/2)

O(n^2)预处理阶乘计算组合数即可(注意娇贵的模数)。

Code:

int n,m,p,q,t;
int ans;
int C[N][N];
inline void pre(int n)
{
	rep(i,0,n)C[i][0]=C[i][i]=1;
	rep(i,1,n)rep(j,i+1,n)C[j][i]=(C[j-1][i-1]+C[j-1][i])%mod;
}
inline int func(int pi,int qi)
{
	if(pi>p||qi>q)return 0;
	int dp=p-pi,dq=q-qi;
	if(dp%2==1||dq%2==1)return 0;
	dp/=2,dq/=2;
	
	int ans=1;
	ans=C[n][pi]*C[m][qi]%mod;
	ans=ans*C[n+dp-1][dp]%mod;
	ans=ans*C[m+dq-1][dq]%mod;
	
	return ans;
}
/*----------------------*/
inline void Main()
{
	cin>>n>>m>>p>>q>>t;
	pre(N-1);
	rep(pi,0,n)rep(qi,0,m)
		if(pi*m+qi*n-2*pi*qi==t)ans=(ans+func(pi,qi))%mod;
	cout<

E.土地规划 

Solution:

模拟几次操作过程可以发现,第i次操作的方格(xi,yi)满足xi+yi=2*i(沿着对角线),共操作i个方格。即第i次会操作(i,i)~(2i,0)的所有i个点,并判断是否染色。

同时对于方格(xi,yi),若(xi-1,yi-1)与(xi-2,yi)均已被染色,此时该方格则不会被染色。以上是对题目的解读。

若只考虑方格是否被染色,可以发现题目具有杨辉三角的性质(将每一对角线视为杨辉三角的一行)。进而对于每一个方格的染色情况,只需要判断在杨辉三角中该位置组合数C(n,k)的奇偶性即可。

性质:对于组合数C(n,k),n&k==k当且仅当C(n,k)为奇数。

对于每个被染色方格的颜色,只需要判断它在哪一次操作被染色即可。

时间复杂度:O(a*b)=O(a^2)

Code:

int t,x,y,a,b;
inline bool che(int i,int j)
{
	int n=(i+j)>>1;
	int k=n*2-i;
	return (n&k)==k;
}
/*----------------------*/
inline void Main()
{
	cin>>t>>x>>y>>a>>b;
	--t;
	per(j,b+y-1,y)
	{
		rep(i,x,a+x-1)
			if((i+j)&1||i2*t)cout<<'.';
			else
			{
				if(!che(i,j))cout<<'.';
				else cout<<(char)('A'+((i+j)/2)%2);
			}
		cout<

F.CTF

Solution:

从小到大枚举i,并在接下来的1<

Code:

int t,ans;
/*----------------------*/
inline void Main()
{
	cin>>t;
	int now=0;
	rep(i,1,30)
	{
		int max_rep=1<

G.希望

Solution:

题意可知两棵树之间的所有n!种连边方式均会被遍历到,求期望值。由于n!的值较大,这里考虑计算答案的式子应当可以把阶乘项消去。

易知A与B树之间连接成长为L的环,一定由A中某条长为La的链,与B中某条长为Lb的链首尾相接而成,其中La+Lb=L-2。考虑A中所有距离为La的点对(ia,ja)有cnta[La]个,B中距离为Lb的点对(ib,jb)有cntb[Lb]个。

对于某一种情况(ia,ja)与(ib,jb),当连接成边时该情况对答案产生贡献,此时其他点的连接方式共(n-2)!种。此外连边同样可以产生贡献。

因此,A中长为La的所有链与B中长为Lb所有链进行拼接的情况,对答案的贡献为2*(n-2)!*cnta[La]*cntb[Lb]。由1到m-3枚举所有合法La,Lb=m-2-La,累加即得到总方案数。由于最终答案会除以n!,因此可以将阶乘项消去,得到方案数最终除以n*(n-1)即可。

问题转化为如何求解树中长为k的点对数量,这里使用树形dp求解。记dp[i][j]为结点i所有子节点中距离为j的结点数,易知dp[i][0]=1,且dp[i][j]=sigma dp[son][j-1]。对于当前结点枚举其子节点并进行dfs,每次dfs某个son后更新cnt[k]+=sigma dp[i][j]*dp[son][k-j-1],最后可以统计出距离为k的所有点对。

时间复杂度:O(n*m)

Code:

int n,m;
int hd[2][N],tot[2];
struct edge
{
	int to,nxt;
	edge()
	{
		to=nxt=0;
	}
}ed[2][N<<1];
inline void add(int u,int v,int tag)
{
	++tot[tag];
	ed[tag][tot[tag]].to=v;
	ed[tag][tot[tag]].nxt=hd[tag][u];
	hd[tag][u]=tot[tag];
}
int dp[2][N][N],cnt[2][N];
void dfs(int now,int pre,int tag)
{
	dp[tag][now][0]=1;
	for(int i=hd[tag][now];i;i=ed[tag][i].nxt)
	{
		int next=ed[tag][i].to;
		if(next==pre)continue;
		dfs(next,now,tag);
		rep(j,0,m-1)
		{
			rep(dis,j+1,m)cnt[tag][dis]+=dp[tag][now][j]*dp[tag][next][dis-j-1];
			if(j>0)dp[tag][now][j]+=dp[tag][next][j-1];
		}
	}
}
/*----------------------*/
inline void Main()
{
	cin>>n>>m;
	rep(tag,0,1)rep(i,1,n-1)
	{
		int x,y;cin>>x>>y;
		add(x,y,tag);add(y,x,tag);
	}
	dfs(1,0,0);dfs(1,0,1);
	
	int res=0;
	rep(d,1,m-3)res+=2*cnt[0][d]*cnt[1][m-d-2];
	double ans=res;
	ans/=(double)(n*(n-1));
	cout<

J.抽奖

Solution:

签到题。首先求出x颗原石的抽奖次数,并得到相应的星辉。然后循环使用星辉抽奖,扣除相应的星辉并添加得到的星辉,直到星辉数目不够抽奖为止。

Code:

int x,y,ans;
/*----------------------*/
inline void Main()
{
	cin>>x;
	ans+=x/160;
	
	y=(ans/10)*3;
	while(y>=5)
	{
		int tmp=ans;
		ans+=y/5;
		y%=5;
		y+=(ans/10-tmp/10)*3;
	}
	cout<

候补

你可能感兴趣的:(Practice,算法)