本场赛时5题,被B和H卡了一下,后面的中档题没时间开。觉得是见的比较少,前面的题难度不高但由于思考和用了比较复杂的方法浪费了不少时间。
本场链接:寒假训练营3
B. Random
考察点: 质数,gcd
之前没遇到过随机生成的题,一直在想特例怎么办。
赛时我用了筛法,由于本题不是让用这个,所以数据规模上不太匹配,我一直没写.
思路
由于数据是随机的,所以非常难遇到一直找不到两个都是不同质数的数,直接 n 2 n^2 n2暴力就行
这里贴上筛法的代码,让本题变得有意义一些
cpp
void pre()
{
v[1]=1;
for(int i=1; i<N; i++)
{
if(!v[i]) f.pb(i);
for(int j=0; j<min(N,(int)f.size())&&i*f[j]<=N; j++)
{
v[i*f[j]]=1;
if(i%f[j]==0) break;
}
}
}
void check(vector<int>&a)
{
unordered_map<int,int> mp;
k=0;
for(int i=0; i<n; i++)
{
int x=a[i];
for(int j=0; j<f.size(); j++)
{
if(x%f[j]==0)
{
if(mp[f[j]])
{
k=1;
cout << mp[f[j]] << ' ' << a[i] << endl;
return;
}
else
{
mp[f[j]]=a[i];
while(x%f[j]==0&&f[j]) x/=f[j];
}
}
if(x==1) break;
}
}
}
void solve()
{
cin >> n;
vector<int> a(n);
map<int,int> p;
for(int &i:a) cin >> i,p[i]++;
for(auto [x,y]:p)
{
if(x!=1&&y>1)
{
cout << x << ' ' << x << endl;
return;
}
}
check(a);
if(!k) cout << -1 << endl;
}
J. Branch of Faith
考察点: 二叉树,计算
思路
法1:二进制的每一位刚好就是二叉树每一层的数量,可利用此特性进行模拟。
遇到的问题:log2计算太大的数字如(1e18)的时候会导致精度丢失,还是一点点左移保险
法2:将二叉树前x层之和存到vector里,每次用lower_bound定位到当层并计算
代码1
cpp
// Problem: Branch of Faith
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/120563/J
// Memory Limit: 512 MB
// Time Limit: 2000 ms
int cal(int x)
{
int len=0;
while(x) x>>=1LL,len++;
int p=(1LL<<len)-1;
int q=(1LL<<len-1)-1;
int res=min(p,n)-q;
return res;
}
void solve()
{
int q;
cin >> n >> q;
while(q--)
{
int x;
cin >> x;
int ans=cal(x);
cout << ans << endl;
}
}
代码2:
cpp
void pre()
{
for(int i=1; i<=INF; i*=2) f.pb(i-1);
}
void solve()
{
cin >> n;
int q;
cin >> q;
while(q--)
{
int x;
cin >> x;
int cu=lower_bound(all(f),x)-f.begin();//当层
int num=1LL<<cu-1;//当层总数量
int k=n-f[cu-1];
cout << min(num,k) << endl;
}
}
F. Energy Synergy Matrix
考察点: 博弈,思维
思维
- 需要注意的是,小红和小小红不是同一个人
- 小红的任务是在小小红所在的另一行在不让小紫放到此行的情况下尽量靠右
- 小紫的任务是在小小红所在的这一行尽量靠左的放
- 大概是这么个情况,其中⚪代表小红,菱形表示小紫

- 先说结论: a n s = ( n − 1 ) + n / 5 ans=(n-1)+n/5 ans=(n−1)+n/5
- 首先
n-1是除了第一列以外向右走的步数 n/5是上下变换的步数。通过观察不难发现,小小红总是在菱形所在的前一列进行变换,但是由于题目中说到达当列的任意位置都行,所以这个推迟了一步,每次就到了菱形这一列。而菱形列刚好是5的倍数。
- 首先
H. Tic Tac DREAMIN'
考察点: 数学,计算几何
思路1
- 用二分不断枚举
x的位置,check中用叉积计算面积来判断。- 需要注意的是,叉积具有方向性,如果 A C → \overrightarrow{AC} AC 在 A B → \overrightarrow{AB} AB 的左侧,则 A B → × A C → > 0 \overrightarrow{AB}×\overrightarrow{AC}>0 AB ×AC >0。所以计算面积需要取绝对值
- 面积公式为 1 2 ⋅ ∣ A B → × A C → ∣ \frac{1}{2}·|\overrightarrow{AB}×\overrightarrow{AC}| 21⋅∣AB ×AC ∣
思路2
- 由于用叉积计算面积的时候
x是唯一的未知数,所以可以直接解出来- 需要注意的是,正着计算叉积的时候直接取绝对值即可,但是倒着推式子就要提前加上符号再推导
代码1:
cpp
double xa,ya,xb,yb;
double g(double x){
return -(yb-ya)*x + (yb-ya)*xa - (xb-xa)*ya;
}
void solve(){
cin>>xa>>ya>>xb>>yb;
if(yb==ya){
double t=fabs((xb-xa)*ya);
if(fabs(t-4.0)<=1e-9) cout<<0.0<<"\n";
else cout<<"no answer\n";
return;
}
double A=-(yb-ya);
double B=(yb-ya)*xa-(xb-xa)*ya;
double x0=-B/A;
double l=x0,r=1e18;
for(int i=0;i<200;i++){
double mid=(l+r)/2;
if(fabs(g(mid))<4.0) l=mid;
else r=mid;
}
cout<<fixed<<setprecision(10)<<((l+r)/2)<<"\n";
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
solve();
return 0;
}
代码2:
cpp
void solve()
{
cin >> xa >> ya >> xb >> yb;
if(ya==yb)
{
double p=fabs(ya)*fabs(xa-xb)/2;
if(p==2) cout << 0.00000 << endl;
else cout << "no answer" << endl;
return ;
}
a=xb*ya-xa*yb;
b=yb-ya;
double x=(-4-a)/b;
cout << fixed << setprecision(15) << x << endl;
}
C. Inverted World
考察点: 思维
思路
- 首先可以明确,最终的结果是
01010101···或者是10101010··· - 我们分别假设最终变成这些串,通过计算后取操作数比较小的那个
- 设原始串是
100011001001,以第二种串10101010···为例:
| 标号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 目标串 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 |
| 原串 | 1 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | 1 |
子串不必连续,找的第一个操作字串就是本来就刚好吻合的,剩下的:
| 标号 | 3 | 6 | 7 | 11 | 12 |
|---|---|---|---|---|---|
| 目标串 | 1 | 0 | 1 | 1 | 0 |
| 原串 | 0 | 1 | 0 | 0 | 1 |
剩下的就一点点"缝合",如果有可以接上的就接上,接不上就另起一列。在这个例子中,我们可以选择3,6,11,12为一个操作字串0101,7是另一个
cpp
int cal(string p)
{
vector<int> v;
for(int i=0; i<s.size(); i++)
if(s[i]!=p[i]) v.pb(s[i]-'0');
if(v.size()==0) return 0;
int val=1,num=v[0];
int cnt0=0,cnt1=0;//此时需要的数
if(v[0]==1) cnt0++;
else cnt1++;
for(int i=1; i<v.size(); i++)
{
if(v[i])
{
cnt0++;
if(cnt1) cnt1--;
else val++;
}
else
{
cnt1++;
if(cnt0) cnt0--;
else val++;
}
}
return val;
}
void solve()
{
cin >> n >> s;
string s1,s2;
char x='0';
for(int i=0; i<n; i++)
{
s1+=x;// 01
x^=1;
s2+=x;// 10
}
int k1=cal(s1);
int k2=cal(s2);
cout << min(k1,k2) << endl;
}
A. 宙天
考察点: 简单语法
思路
- 利用 s q r t sqrt sqrt下取整的性质, k = s q r t ( x ) k=sqrt(x) k=sqrt(x),直接判断 k ( k + 1 ) k(k+1) k(k+1)与 x x x是否相等即可
G. スピカの天秤
考察点: 简单模拟
思路
- 分类讨论,对每种情况进行模拟,等到了下一种状态就输出
本次补题主要有这几点:
- B题随机二字背后的深意
- J题学会多利用二进制的性质
- F题比较有意思的思维博弈和找规律
- H题对叉积的进一步理解
- C题对题目的不断拆解和转化