牛客周赛60
注释:
-
*角标表示比赛中我没有做出来的题目,
-
**表示我没有做出来,我也觉得我掌握很困难的题目。
希望给刚入门的一些帮助。
A. 困难数学题
题目描述略。
这题没啥意思,我们知道两个数互相异或是肯定0的,所以直接cout一个0就行了,比较无聊。
B. 构造序列
题目描述略。
正正不能相邻,负负不能相邻,就是说必须一正一负串成串儿。
那么长度应该是两两一组后多一个,或者正负相同数目的情况下直接就是2n。
#include<iostream>
using namespace std;
int main(){
int n, m;
cin>>n>>m;
cout<<min(n, m)*2+(n!=m);
return 0;
}
C. 连点成线
题目描述略。
让把横纵坐标相等的连成线。那么我们直接就把他们存成结构体,然后按x排个序算一遍最大值,然后再按y排个序算一遍最大值,写两个sort的cmp就解决了。
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+100;
struct node{
int x, y;
}a[N];
int n, m;
bool cmp1(node a, node b){
return a.x<b.x || (a.x==b.x && a.y<b.y);
}
bool cmp2(node a, node b){
return a.y<b.y || (a.y==b.y && a.x<b.x);
}
int net1(){
int sig=-1, sy=0;
int res=0;
for(int i=1; i<=m; ++i){
if(sig==-1 || sig!=a[i].x){
sy=a[i].y;
sig=a[i].x;
}
if(sig==a[i].x){
res=max(res, a[i].y-sy);
}
}
return res;
}
int net2(){
int sig=-1, sx=0;
int res=0;
for(int i=1; i<=m; ++i){
if(sig==-1 || sig!=a[i].y){
sx=a[i].x;
sig=a[i].y;
}
if(sig==a[i].y){
res=max(res, a[i].x-sx);
}
}
return res;
}
int main(){
// int n, m;
cin>>n>>m;
for(int i=1; i<=m; ++i){
cin>>a[i].x>>a[i].y;
}
sort(a+1, a+m+1, cmp1);
int ans=net1();
sort(a+1, a+m+1, cmp2);
ans=max(net2(), ans);
cout<<ans<<endl;
return 0;
}
D. 我们N个真是太厉害了
题目描述略。
这道题非常的奇特,我第一眼以为是背包dp(动态规划)。
事实上它确实是dp,但是不是背包。
那么这个东西怎么分析呢。
我们手玩一下样例 1.1 1.1 1.1,我们发现,数据和位置无关,而且和找钱一样的道理,面值越小越不可替代。所以直接从小到大排个序。
然后验证一下是不是真的不可替代。手玩一下样例 2 , 3 , 4 , 5 2,3,4,5 2,3,4,5,我们发现 1 1 1确实是不能表示,所以肯定要有 a 1 = 1 a_1=1 a1=1
那么关注一下 a 2 a_2 a2,如果 a 2 = k a_2=k a2=k,我们能表示 k + 1 k+1 k+1和 k k k和 1 1 1,如果此时还没看出来再来一个 a 3 = k 2 a_3=k_2 a3=k2,那么就可以表示成 1 , k , k + 1 , k 2 , k + k 2 , k + k 2 + 1 1,k,k+1,k_2, k+k_2, k+k_2+1 1,k,k+1,k2,k+k2,k+k2+1
我们知道 k ≤ k 2 ⇒ k = 1 o r 2 k\leq k_2 \Rightarrow k=1 \, or\, 2 k≤k2⇒k=1or2,那么也就是说,第 i i i位置的至少完成生成到 i i i的任务 = = 生成想法 ⇒ ==^{生成想法}\Rightarrow ==生成想法⇒假设 第 i 个数能生成到 k 第i个数能生成到k 第i个数能生成到k = = 推导 = ⇒ ==^{推导}=\Rightarrow ==推导=⇒第 i + 1 i+1 i+1个数能生成 a i ∼ k + a i a_i\sim k+a_i ai∼k+ai。然后前面生成了 0 ∼ k 0\sim k 0∼k,如果 k + 1 < a i k+1 < a_i k+1<ai就直接寄了对吧。如果他俩无缝衔接就接着算并且宣布第 i + 1 i+1 i+1个数能生成到 k + a i k+a_i k+ai。
如果有一瞬间能 n ≤ k n \leq k n≤k就结束了
很明显不需要收尾。
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+100;
int a[N];
void solve(){
int n;
cin>>n;
for(int i=1; i<=n;++i){
cin>>a[i];
}
sort(a+1, a+n+1);
int sum=0;
for(int i=1; i<=n; ++i){
if(a[i]-sum >1){
cout<<sum+1<<endl;
return ;
}
sum=sum+a[i];
if(sum>=n){
cout<<"Cool!"<<endl;
return ;
}
}
}
int main(){
int t;
cin>>t;c
while(t--)solve();
return 0;
}
E. 折返跑
题目略。
题目思路
这个题有个很明显的转化,就是转化为组合数学问题:在 [ 2 , n ] [2,n] [2,n]上画个线,三八线左面是左杆子,右面是右杆子,然后就直接 C l e f t m / 2 × C r i g h t m / 2 C_{left}^{m/2}\times C_{right}^{m/2} Cleftm/2×Crightm/2然后枚举线就行。但是这道题没给你限制那个总样例,这么写会被T掉。
所以要在这个上面再做转化。
很明显你就能想到,既然左面和右面的那个数量都是一定的,是不是只要把坑占好,自动就一半给左杆一半给右杆了。
因此这就是一个典型的球盒问题了。直接计算 C n − 2 m − 1 C_{n-2}^{m-1} Cn−2m−1就可以了。
题目思路—逆元
然后难点就是,这个b数怎么算的问题了对吧,而且还要模一个 M M M。乘我们很好解决,直接一边 × \times ×一边 m o d mod mod就完事了对吧。除怎么解决。
这里的解决方案是逆元 a ∗ a − 1 ≡ 1 ( m o d M ) a*a^{-1}\equiv 1(mod\, M) a∗a−1≡1(modM)。
理由是这样的:
∵ ( a % M × c % M ) % M = ( a × c ) % M , 又 ∵ a ∗ a − 1 ≡ 1 ( m o d M ) \because (a\%M \times c \% M)\%M=(a\times c)\%M, 又\because a*a^{-1}\equiv 1(mod \, M) ∵(a%M×c%M)%M=(a×c)%M,又∵a∗a−1≡1(modM)
∴ ( c / a ) ∗ 1 % M = ( c / a ) ∗ a ∗ a − 1 % M = c ∗ a − 1 % M \therefore (c/a)*1\%M=(c/a)*a*a^{-1}\%M=c*a^{-1}\%M ∴(c/a)∗1%M=(c/a)∗a∗a−1%M=c∗a−1%M
所以我们只需要知道这个数的逆元就能完成这个操作。
然后聊一聊怎么取逆元。
有一个东西叫做费马小定理,懒得翻没关系,它主要说了一个事情就是一个数 a a a关于 M M M的逆元就是 a M − 2 a^{M-2} aM−2,不用知道怎么证,这个知识对正常人的发散性很弱,直接背就行了。
题目思路—快速幂
好了来吧, M = 1 0 9 + 7 M=10^9+7 M=109+7这玩意用循环算一算一个不吱声包超时的。
答案是快速幂,这个操作是怎么做的呢。
我们如果想计算 a 7 a^{7} a7,怎么计算呢。正常人是循环计算的。O人是这样计算的: a 7 = a 1 ∗ a 2 ∗ a 4 = a 1 ∗ ( a 1 ) 2 ∗ ( ( a 1 ) 2 ) 2 a^7=a^1*a^2*a^4=a^1*(a^1)^2*((a^1)^2)^2 a7=a1∗a2∗a4=a1∗(a1)2∗((a1)2)2,看到这个式子有没有一点感觉了对吧。也就是说,我们直接把 M M M变成二进制,那么我们只需要用一个 k k k记录每一个数位,然后每次迭代 k = k 2 k=k^2 k=k2,然后如果当前数位 M M M是 1 1 1那么就将答案 ∗ k *k ∗k,由于就算是 M = 1 0 18 M=10^{18} M=1018(这几乎是longlong的最大值)换成二进制也就只有63位,几乎是 O ( 1 ) O(1) O(1)的。
然后这里有两个板子需要读者背熟并敲熟。直接贴在下面了。
题目需求—模板
这是求 a a a关于 m o d mod mod的逆元的板子
long long inv(long long a, long long mod){
long long res=1;
long long k=a;
long long g=mod-2;
while(g!=0){
if(g&1){
res*=k;
res%=mod;
}
k*=k;
k%=mod;
g>>=1;
}
res%=mod;
return res;
}
把这个板子换个写法就是快速幂 ( a b ) % m o d (a^b)\%mod (ab)%mod的板子
long long inv(long long a, long long b, long long mod){
long long res=1;
long long k=a;
while(b!=0){
if(b&1){
res*=k;
res%=mod;
}
k*=k;
k%=mod;
b>>=1;
}
res%=mod;
return res;
}
这两个板子请大家用熟,这很重要。
好了,这两个问题解决掉了,思路也说完了,所以直接贴码了。
#include<iostream>
using namespace std;
const long long mod=1000000007;
const int N=1e6+7;
int a[N];
long long cob[N];
long long tub[N];
long long inv(long long a, long long mod){
long long res=1;
long long k=a;
long long g=mod-2;
while(g!=0){
if(g&1){
res*=k;
res%=mod;
}
k*=k;
k%=mod;
g>>=1;
}
res%=mod;
return res;
}
long long phi[N];
long long c(int n, int m){
if(m==0)return 1;
if(m>n-m)m=n-m;
long long res=1;
for(int i=1, j=n; i<=m; ++i, --j){
res*=j;
// cout<<j<<' '<<i<<endl;
res%=mod;
res*=phi[i];
res%=mod;
}
return res;
}
void solve(){
int n, m;
cin>>n>>m;
cout<<c(n-2, m-1)<<endl;
}
int main(){
for(int i=1; i<N; ++i) phi[i]=inv(i, mod);
int t;
cin>>t;
while(t--)solve();
return 0;
}
F. 口吃**
题目意思略掉。
这道题需要一点概率论知识。
因为这道题的 i d e a idea idea是官解,所以我也不太知道究竟用什么方法可以想出来。请大家自行体会。
我们认定 f [ i ] f[i] f[i]表示从第 i i i个字开始,说完所有话的期望。
那么先看第一个字,因为f1可以跳转到f1,也可以跳转到f2,并且跳转之后我们就多说了一个字,所以计算的方式是:
f 1 = a 1 a 1 + b 1 f 1 + a 1 a 1 + b 1 f 2 + 1 ⇒ f 1 = f 2 + a 1 + b 1 a 1 ( 1 ) f_1=\frac{a_1}{a_1+b_1}f_1+\frac{a_1}{a_1+b_1}f_2+1\Rightarrow f_1=f_2+\frac{a_1+b_1}{a_1} \qquad(1) f1=a1+b1a1f1+a1+b1a1f2+1⇒f1=f2+a1a1+b1(1)
同理观察第 i i i个字:
f i = a i 2 ( a i + b i ) 2 f i + 1 + 2 a i b i ( a i + b i ) 2 f i + b i 2 ( a i + b i ) 2 f i − 1 + 1 ⇒ a i 2 + b i 2 ( a i + b i ) 2 f i = a i 2 ( a i + b i ) 2 f i + 1 + b i 2 ( a i + b i ) 2 f i − 1 + 1 ( 2 ) f_i=\frac{a_i^2}{(a_i+b_i)^2}f_{i+1}+\frac{2a_ib_i}{(a_i+b_i)^2}f_{i}+\frac{b_i^2}{(a_i+b_i)^2}f_{i-1}+1\Rightarrow \frac{a_i^2+b_i^2}{(a_i+b_i)^2}f_i=\frac{a_i^2}{(a_i+b_i)^2}f_{i+1}+\frac{b_i^2}{(a_i+b_i)^2}f_{i-1}+1\qquad(2) fi=(ai+bi)2ai2fi+1+(ai+bi)22aibifi+(ai+bi)2bi2fi−1+1⇒(ai+bi)2ai2+bi2fi=(ai+bi)2ai2fi+1+(ai+bi)2bi2fi−1+1(2)
然后我们思索这样的一个问题。
我们从 f i f_i fi开始推,我们一定能把这个式子推导成只有 f i + 1 f_{i+1} fi+1和 f 1 f_1 f1两个含 f f f剩下都是已知常数的一个式子。那么我们就可以这么搞了:直接把所有的已知常数和f_1一起混成两个参量P,Q,直接把这个式子写成 f i = P i f i + 1 + Q 1 f_i=P_if_{i+1}+Q_1 fi=Pifi+1+Q1的形式。那么很明显的由 ( 1 ) (1) (1), P 1 = 1 , Q 1 = a 1 + b 1 a 1 P_1=1, Q_1=\frac{a_1+b_1}{a_1} P1=1,Q1=a1a1+b1
那么我们继续推导 ( 2 ) (2) (2):
a i 2 + b i 2 ( a i + b i ) 2 f i = a i 2 ( a i + b i ) 2 f i + 1 + b i 2 ( a i + b i ) 2 f i − 1 + 1 ⇒ a i 2 + b i 2 ( a i + b i ) 2 f i = a i 2 ( a i + b i ) 2 f i + 1 + b i 2 ( a i + b i ) 2 ( P i − 1 f i + Q i − 1 ) + 1 ⇒ a i 2 + b i 2 − b i 2 P i − 1 ( a i + b i ) 2 f i = a i 2 ( a i + b i ) 2 f i + 1 + Q i − 1 ( a i + b i ) 2 + 1 ⇒ f i = a i 2 a i 2 + b i 2 − b i 2 P i − 1 f i + 1 + b i Q i − 1 + ( a i + b i ) 2 a i 2 + b i 2 − b i 2 P i − 1 \frac{a_i^2+b_i^2}{(a_i+b_i)^2}f_i=\frac{a_i^2}{(a_i+b_i)^2}f_{i+1}+\frac{b_i^2}{(a_i+b_i)^2}f_{i-1}+1\\\Rightarrow \frac{a_i^2+b_i^2}{(a_i+b_i)^2}f_i=\frac{a_i^2}{(a_i+b_i)^2}f_{i+1}+\frac{b_i^2}{(a_i+b_i)^2}(P_{i-1}f_i+Q_{i-1})+1\\\Rightarrow\frac{a_i^2+b_i^2-b_i^2P_{i-1}}{(a_i+b_i)^2}f_i=\frac{a_i^2}{(a_i+b_i)^2}f_{i+1}+\frac{Q_{i-1}}{(a_i+b_i)^2}+1\\\Rightarrow f_i=\frac{a_i^2}{a_i^2+b_i^2-b_i^2P_{i-1}}f_{i+1}+\frac{b_iQ_{i-1}+(a_i+b_i)^2}{a_i^2+b_i^2-b_i^2P_{i-1}} (ai+bi)2ai2+bi2fi=(ai+bi)2ai2fi+1+(ai+bi)2bi2fi−1+1⇒(ai+bi)2ai2+bi2fi=(ai+bi)2ai2fi+1+(ai+bi)2bi2(Pi−1fi+Qi−1)+1⇒(ai+bi)2ai2+bi2−bi2Pi−1fi=(ai+bi)2ai2fi+1+(ai+bi)2Qi−1+1⇒fi=ai2+bi2−bi2Pi−1ai2fi+1+ai2+bi2−bi2Pi−1biQi−1+(ai+bi)2
然后到这里为止。我们就知道了新的 P i 和 Q i P_{i}和Q_i Pi和Qi的求法了,即 P i = a i 2 a i 2 + b i 2 − b i 2 P i − 1 , Q i = b i Q i − 1 + ( a i + b i ) 2 a i 2 + b i 2 − b i 2 P i − 1 P_i=\frac{a_i^2}{a_i^2+b_i^2-b_i^2P_{i-1}}, Q_i=\frac{b_iQ_{i-1}+(a_i+b_i)^2}{a_i^2+b_i^2-b_i^2P_{i-1}} Pi=ai2+bi2−bi2Pi−1ai2,Qi=ai2+bi2−bi2Pi−1biQi−1+(ai+bi)2
最后,我们知道 f n = 1 f_n=1 fn=1
因此通过每次迭代 f i = P i ∗ f i + 1 + Q i f_i=P_i*f_{i+1}+Q_i fi=Pi∗fi+1+Qi求得 f 1 f_1 f1为题目答案。
#include<iostream>
using namespace std;
const int mod=1e9+7;
typedef long long ll;
ll inv(ll x){
ll m=mod-2;
ll k=x;
ll res=1;
while(m!=0){
if(m&1)res*=k, res%=mod;
k*=k;
k%=mod;
m/=2;
}
return res;
}
struct node{
ll x;
friend node operator*(node a, node b){
return {a.x*b.x%mod};
}
friend node operator/(node a, node b){
return {a.x*inv(b.x)%mod};
}
friend node operator+(node a, node b){
return {(a.x+b.x)%mod};
}
friend node operator-(node a, node b){
return {(a.x-b.x+mod)%mod};
}
};
const int N=1e5+100;
node a[N], b[N];
node p[N], q[N];
node f[N];
int main(){
int n;
cin>>n;
for(int i=1; i<n; ++i)cin>>a[i].x;
for(int i=1; i<n; ++i)cin>>b[i].x;
p[1]={1};
q[1]=(a[1]+b[1])/a[1];
for(int i=2; i<n; ++i){
node A=a[i]*a[i];
node B=b[i]*b[i];
node S=(a[i]+b[i])*(a[i]+b[i]);
node M=A+B-p[i-1]*B;
p[i]=A/M;
q[i]=(B*q[i-1]+S)/M;
}
f[n]={1};
for(int i=n-1; i>=1; --i){
f[i]=f[i+1]*p[i]+q[i];
}
cout<<f[1].x;
return 0;
}
赛后总结
这次题目感觉比小白月赛101要难,但是前五题非常经典,实例比较代表性。F确实很复杂,期望dp确实没有很常遇到过。感谢牛客respect。