T1 数列
给定⼀个正整数 ,输出外观数列的第 项。
外观数列是⼀个整数序列,从数字 开始,序列中的每⼀项都是对前⼀项的描述。
你可以将其视作是由递归公式定义的数字字符串序列:
countAndSay(1) = “1”
countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另⼀个数字字符串。
前五项如下:
- 1
- 11
- 21
- 1211
- 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(n−1)
哎哎还真是
好那只要求出卡特兰数就有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(2n−1)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分
很满意了