ZROI 2021/8/29 考试总结

T1 数列

给定⼀个正整数 ,输出外观数列的第 项。
外观数列是⼀个整数序列,从数字 开始,序列中的每⼀项都是对前⼀项的描述。
你可以将其视作是由递归公式定义的数字字符串序列:
countAndSay(1) = “1”
countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另⼀个数字字符串。
前五项如下:

  1. 1
  2. 11
  3. 21
  4. 1211
  5. 111221

第⼀项是数字 1
描述前⼀项,这个数是 1 即 “ ⼀ 个 1 ”,记作 11
描述前⼀项,这个数是 11 即 “ ⼆ 个 1 ” ,记作 21
描述前⼀项,这个数是 21 即 “ ⼀ 个 2 + ⼀ 个 1 ” ,记作 1211
描述前⼀项,这个数是 1211 即 “ ⼀ 个 1 + ⼀ 个 2 + ⼆ 个 1 ” ,记作 111221
要描述⼀个数字字符串,⾸先要将字符串分割为最⼩数量的组,每个组都由连续的最多相同字符组成。
然后对于每个组,先描述字符的数量,然后描述字符,形成⼀个描述组。要将描述转换为数字字符串,
先将每组中的字符数量⽤数字替换,再将所有描述组连接起来。

考试思路

按题意模拟

#include<bits/stdc++.h>
using namespace std; 
const int N = 5e5+7;
int a[30][N];
int n,m;
int main()
{
	cin>>n;
	a[1][1]=1;
	m=1;
	for(int i=2;i<=n;i++)
	{
		int len=0,t=0;
		for(int j=1;j<=m+1;j++)
		{
			if(j!=1&&a[i-1][j]!=a[i-1][j-1])
			{
				a[i][++t]=len;
				a[i][++t]=a[i-1][j-1]; 
				len=0;
			}
			len++;
		}
		m=t;
	}
	for(int i=1;i<=m;i++)
	printf("%d",a[n][i]);
	return 0;
}

期望得分: 100
实际得分: 100

T2 索引

给定⼀个由 个不同整数组成的严格递增的数组 ,请问是否存在⼀个索引 使得A[i] = i成⽴。
⼀共有K 次询问。从第⼆次询问开始,第 t次询问会在第t-1 次询问的数组 的基础上进⾏⼀次区间修改。每次修改给出三个整数 ,L,R,C,代表在L~R区间内加C

考试思路

一开始着实没什么思路,主要是n比较大,线段树减不了,但是突然想到树状数组不用建树,而且数组严格递增,可以二分查找,然后就没了

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e7+10;
LL c[N];
int m,n;
int lowbit(int x)
{
	return x&(-x);
}
void add(int x,int v)
{
	for(int i=x;i<=n;i+=lowbit(i))
	c[i]+=v;
}
LL ask(int x)
{
	LL res=0;
	for(int i=x;i;i-=lowbit(i))
	res+=c[i];
	return res;
}
int a[N],b[N];
inline int read()
{
	int X=0; bool flag=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
	while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
	if(flag) return X;
	return ~(X-1);
}
bool check()
{
	int l=1,r=n,mid,ans=-1;
	while(l<=r)
	{
		mid=(l+r)>>1;
		if(ask(mid)>=0)
		{
			ans=mid;
			r=mid-1;
		}
		else l=mid+1; 
	}
	if(ans==-1) return 0;
	if(ask(ans)==0) return 1;
	return 0;
} 
int main()
{
	cin>>m>>n;
	m--;
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
		a[i]=a[i]-i;
		b[i]=a[i]-a[i-1];
		if(b[i]!=0) add(i,b[i]);	
	} 		
	if(check()) printf("YES\n");
	else printf("NO\n"); 
	while(m--)
	{
		int L,R,C;
		L=read();
		R=read();
		C=read();
		add(L,C);
		add(R+1,-C);
		if(check()) printf("YES\n");
		else printf("NO\n"); 
	}
	return 0;
}

期望得分:70分
实际得分:100分

T3 奇数

Q次询问,每次询问给出⼀个区间 ,回答区间内[L,R] 出现次数为奇数 的数的个数。

考试思路

好像原来做过求区间偶数个数,原理一样
都是分块
码……码……码
码……码……码
诶好像要离散化
码……码……码
码……码……码
码完了,过了

真实结果

哎呀我的分块被卡常了
标称用的是常数较小的莫队
哎让我把块的大小调一下
过了?!

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define cov(x,v) memset(x,v,sizeof(x))
const int N = 1e5+100;
const int sqN = 1e3+7;
const double K = 0.7;
int sum[sqN][N],ans[sqN][sqN];
LL a[N];
int st[sqN],ed[sqN],pos[N],len,cnt;
int t[N];
int n,m,last;
int dct[N],c;
void Pre_sum()
{
	for(int i=1;i<=cnt;i++)
	{
		for(int j=0;j<=c;j++) sum[i][j]=sum[i-1][j];
		for(int j=st[i];j<=ed[i];j++)
		sum[i][a[j]]++;
	}
}
void Pre_ans()
{
	for(int i=1;i<=cnt;i++)
	{
		for(int j=i;j<=cnt;j++)
		{
			ans[i][j]=ans[i][j-1];
			for(int k=st[j];k<=ed[j];k++)
			{
				t[a[k]]++;
				if((t[a[k]]&1)) ans[i][j]++;
				else ans[i][j]--;
			}
		}
		cov(t,0);
	}
}
int query(int l,int r)
{
	if(pos[r]-pos[l]<=1)
	{
		int Ans=0;
		for(int i=l;i<=r;i++)
		{
			t[a[i]]++;
			if((t[a[i]]&1)) Ans++;
			else  Ans--;
		}
		for(int i=l;i<=r;i++)
		t[a[i]]--;
		return Ans;
	}
	int Ans=ans[pos[l]+1][pos[r]-1];
	for(int i=l;i<=ed[pos[l]];i++)
	{
		t[a[i]]++;
		int times=sum[pos[r]-1][a[i]]-sum[pos[l]][a[i]];
		if(((t[a[i]]+times)&1)) Ans++;
		else Ans--;
	}
	for(int i=st[pos[r]];i<=r;i++)
	{
		t[a[i]]++;
		int times=sum[pos[r]-1][a[i]]-sum[pos[l]][a[i]];
		if(((t[a[i]]+times)&1)) Ans++;
		else  Ans--;
	}
	for(int i=l;i<=ed[pos[l]];i++) t[a[i]]--;
	for(int i=st[pos[r]];i<=r;i++) t[a[i]]--;
	return Ans;
}
inline int read()
{
	int X=0; bool flag=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
	while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
	if(flag) return X;
	return ~(X-1);
}
bool cmp(LL x,LL y)
{
	return x<y;
}
int main()
{
	n=read();
	len=K*sqrt(n*1.0);
	cnt=(n-1)/len+1;
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
		dct[i]=a[i];
		pos[i]=(i-1)/len+1;
		if(st[pos[i]]==0) st[pos[i]]=i;
		ed[pos[i]]=i;
	}
	sort(dct+1,dct+n+1,cmp);
	c=unique(dct+1,dct+n+1)-dct-1;
	for(int i=1;i<=n;i++)
	a[i]=lower_bound(dct+1,dct+c+1,a[i])-dct;
	Pre_sum();
	Pre_ans();
	m=read();
	while(m--)
	{
		int l,r;
		l=read();
		r=read();
		printf("%d\n",query(l,r));
	} 
	return 0;
} 

期望得分:100分
实际得分:90分

T4 解谜

现在有一个解谜游戏。这个解谜游戏的地图上一共有 NN 个房间,形成了一棵 NN 个点的有根二叉树,而在二叉树的每个叶子节点上都有一条解谜的线索。

现在想知道对于所有可能出现的包含 NN 个房间的地图中,解谜线索的期望条数(每种可能出现的地图等概率出现)。

可以证明,线索的期望条数一定是一个有理数 pqpq ,你只需要输出这个数在模 21484736472148473647 意义下的余数即可。

如果你没有学过如何对一个分数进行取模,你可以认为本题要你输出的是 p⋅qmod−2p⋅qmod−2 (其中 mod=2148473647mod=2148473647) 在模 21484736472148473647 意义下的余数。

考试思路

期望?完蛋了
算了还是看一眼吧
根据期望的定义,很容易得出
E ( n ) = w [ 1 ] × C [ 1 ] F [ n ] + w [ 2 ] × C [ 2 ] F [ n ] … … E(n)=w[1]\times \frac{C[1]}{F[n]}+w[2]\times \frac{C[2]}{F[n]}…… E(n)=w[1]×F[n]C[1]+w[2]×F[n]C[2]
其中w代表可能的叶子数量,C代表这个叶子数量的出现次数,F代表n个节点的二叉树的形态总数
分母是一样的,用分配率乘起来
E ( n ) = G ( n ) F ( n ) E(n)=\frac{G(n)}{F(n)} E(n)=F(n)G(n)
其中G的含义是所有可能的叶子数目之和,重复的也要算
众所周知
n个节点的二叉树个数即为卡特兰数第n项,这就是F
但是这个G好像不好求
打个表吧
哎哎哎
G ( n ) G(n) G(n)好像等于 n × F ( n − 1 ) n\times F(n-1) n×F(n1)
哎哎还真是
好那只要求出卡特兰数就有70pts了

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL N = 2e7+7;
const LL mod = 2148473647;
LL Jc[N],Ny[N];
LL Pow(LL a,LL b)
{
	LL res=1;
	while(b)
	{
		if(b&1) res=res*a%mod;
		a=a*a%mod;
		b=b/2;
	}
	return res%mod;
}
void Pre(LL n)
{
	Jc[1]=1;
	Jc[0]=1;
	for(LL i=2;i<=n;i++)
	Jc[i]=Jc[i-1]*i%mod;
	Ny[n]=Pow(Jc[n],mod-2)%mod;
	for(LL i=n-1;i>=1;i--)
	Ny[i]=Ny[i+1]*(i+1)%mod;
	Ny[0]=1;
}
LL C(LL n,LL m)
{
	return Jc[n]*Ny[m]%mod*Ny[n-m]%mod;
}
LL n;
int main()
{
	cin>>n;
	Pre(2*n+1);
	LL p=C(2*n,n),q=(n+1)*(C(2*n-2,n-1))%mod;
	LL res=q*Pow(p,mod-2)%mod;
	printf("%lld",res);
	return 0;
}

正解

哎呀把上个式子化简好像就能过了
答案为 n ( n + 1 ) 2 ( 2 n − 1 ) \frac{n(n+1)}{2(2n-1)} 2(2n1)n(n+1)
……
Ps:震惊竟然有人5分钟A了这题
没了

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL N = 2e7+7;
const LL mod = 2148473647;
LL Jc[N],Ny[N];
LL Pow(LL a,LL b)
{
	LL res=1;
	while(b)
	{
		if(b&1) res=res*a%mod;
		a=a*a%mod;
		b=b/2;
	}
	return res%mod;
}
LL n;
int main()
{
	cin>>n;
	LL p=n*(n+1)%mod;
	LL q=2*(2*n-1)%mod;
	LL res=p*Pow(q,mod-2)%mod;
	printf("%lld",res);
	return 0;
}

期望得分:70分
实际得分:100分

考试总结

整体还不错
该拿的分基本都拿了
第四题最不擅长的期望也通过分析与打表拿了70分
很满意了

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值