暑假集训D20 2023.8.15 contest E NWERC 2021
A.Access Denied
题意:给出系统密码比对的程序和每条指令所耗费的时间,你需要通过程序返回的执行时间来破解出正确的密码.
\(\operatorname{Solution}\)
由题目检查密码的程序.可以先猜长度再猜每个字符.长度错误会耗费 \(5ms\) .直到返回的时间不是 \(5ms\) 说明长度猜对了.
然后猜每个字符.根据判断的程序.首先判断长度花费了 \(4ms\).
然后每次 \(for\) 循环都要花费 \(5ms\) (赋值或i++
花费 \(1ms\) ,比较花费 \(3ms\) ,for分支花费 \(1ms\) )
进入循环后,判断字符时,如果不等于则花费 \(5ms+4ms\) ,如果相等花费 \(5ms+5ms\) ,不妨假设前 \(k\) 个字符相等,第 \(k+1\) 个字符不等,那么花费时间就是 \(4+9*k+10ms\).
每次枚举每个位置的字母即可.返回的时间 \(x\) ,代表下标在 \(x/9-1\) 上的字母出错了,枚举即可.
时间复杂度 \(\operatorname{O}(2n)\)
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
vector<char> p;
int check(string t)
{
cout<<t<<endl;
string str;
cin>>str;
cin>>str;
if(str=="GRANTED")
{
exit(0);
}
int x;
scanf(" (%d",&x);
// cout<<"x:"<<x<<endl;
cin>>str;
return (x/9) -1;
}
int main()
{
string s = "a";
int len =1;
for(int i= 'a';i<='z';i++)
p.pb(i);
for(int i= 'A';i<='Z';i++)
p.pb(i);
for(int i= '0';i<='9';i++)
p.pb(i);
// for(char j:p)
// {
// cout<<j<<endl;
// }
int x;
while(1)
{
cout<<s<<endl;
string str;
cin>>str;
// cout<<str<<' ';
cin>>str;
// cout<<str<<' ';
if(str=="GRANTED")exit(0);
scanf(" (%d",&x);
// cout<<x<<' ';
cin>>str;
// cout<<str<<'\n';
if(x!=5)break;
len++;
s+="a";
}
// cout<<len<<endl;
int i = x/9 - 1;
// cout<<i<<endl;
for(;i<len;)
{
for(char j :p)
{
// cout<<"j:"<<j<<endl;
s[i] = (char)j;
int temp = check(s);
// cout<<"temp:"<<temp<<endl;
if(temp!=i)
{
i = temp;
break;
}
}
}
return 0;
}
J.Jet Set
题意:一个人沿着环球航行,给出每个景点的经纬度,这个人从第一个点出发,依次走到最后一个点,再从最后一个点回到起点.注意每两个点之间,这个人只会走近的那一条路.问这个人能否完成环球航行?(所有的经度都被走过)
\(\operatorname{Solution}\)
首先由于度数是 \(-180\sim180\) ,负数不好处理,将他们偏移到 \(0\sim 360\) .另外,如果
考虑差分,对于两个点的经度 \(l\) 和经度 \(r\) ,如果从 \(l\) 到 \(r\) 那么分为 \(r-l\) 大于180度和小于180度的情况.如果小于很简单,直接让 \(l\sim r\) 之间的数 \(+1\) 即可(采用差分很容易实现),如果大于,则是 \(0\sim l,r\sim360\) 的数 \(+1\) .如果他们两个正好相差 180 度,也就是正好相差一个半球,那么若这一边没有走过,就走这边,如果这边走过了,就走另一边个半球,否则重复的走会使得原本能恰好走完的路径导致最后有一边没有走.
另外还有一种情况,比如 \(0\sim -179 \sim 179 \sim 1 \sim 180 \sim -179 \sim 0\),这样虽然走完了 \(-180\sim 0 ,1\sim 180\),但是 $0\sim1中间的没有走(如0.5),直接用差分的话,只能检测到 \(0,1\) 都走了,检测不出来 \(0.5\) 有没有走,简便的解决方法是把所有度数变为原来的两倍.最后得结果时除以 \(2\) 即可.
时间复杂度 \(\operatorname{O}(n)\)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int a[N],x[N],y[N];
int m = 720;
void add(int l,int r)
{
a[l]++;
a[r+1]--;
}
int getsum(int l)
{
int res = 0;
for(int i = 0;i<=l;i++)
res+=a[i];
return res;
}
int check(int l,int r)
{
int res = 0;
for(int i = 0;i<=r;i++)
{
res+=a[i];
if(res==0&&i>=l)
{
return 0;
}
}
return 1;
}
void solve(int p,int q)
{
int a = p,b = q;
if(a>b)swap(a,b);
if(b-a>m/2)
{
add(0,a),add(b,m);
}
else if(b-a==m/2)
{
if(!check(a,b))//如果他们俩差了半个半球,并且一边没有走过,就走这边,如果这边走过了,就走另一边个半球
{
add(a,b);
}
else add(0,a),add(b,m);
}
else add(a,b);
}
signed main(int argc, char const *argv[])
{
int n;
cin>>n;
for(int i= 1;i<=n;i++)
{
cin>>x[i]>>y[i];
y[i]= y[i]*2+360;
}
y[n+1] = y[1];
for(int i= 2;i<=n+1;i++)
{
solve(y[i-1],y[i]);
}
int sum = 0;
for(int i = 0;i<=720;i++)
{
sum+=a[i];
if(sum==0)
{
cout<<"no "<<fixed<<setprecision(1)<<(double)(i-360)/2;
exit(0);
}
}
cout<<"yes";
return 0;
}
I.IXth Problem
给定一定个数的罗马字母,问能组合出来最少的罗马数字的个数是多少?(每个字母都必须用到,给出组合的方法和每种组合方法的个数.可能有多种组合方法,给出任意一种合法解即可)
\(\operatorname{Solution}\)
考虑二分答案.那么问题转化成了给定整数 \(n\) ,要组合出来 \(n\) 个罗马数字最多能用多少个罗马字母.
那么只需要对 \(n\) 个组往里面依次装填罗马字母 \(M,D,C,L,X,V,I\) 即可.按如下规则填入:
首先三个三个填入每一组.如果不够了,就用该字母和下一个字母组合起来填入,比如 \(M\) 不够三个一组填完,就用 \(CM\) 填.
如果可以用完所有字母,那么就是合法解.
当数量很大时,可以通过给各组分相同的分法来加速这一过程.
时间复杂度 \(\operatorname{O(log}n)\)
L.Lucky Shirt
有 \(n\) 件T恤自上而下放在衣柜里,每天从衣柜中最上面取出一件T恤穿,每天晚上穿完后放到洗衣篮里面.这个人要洗 \(k\) 轮衣服(只洗洗衣篮里的衣服,今天穿的衣服也会洗),每次洗衣服的间隔是完全均匀随机的(也即第 \(j\) 天随机洗前 \(l_j\) 件),洗完衣服后随机地放回衣柜最顶部.已知幸运T恤的初始位置是 \(i\) ,洗完 \(k\) 轮衣服后,幸运T恤的期望位置是多少?
\(\operatorname{Solution}\)
如果这 \(k\) 次每次都选的前 \(1\sim i-1\) 的某个位置,那么幸运T恤的最终位置还是 \(i\) ,概率 \(p= (\frac{i-1}{n})^k\)
如果这 \(k\) 次选的位置存在大于等于 \(i\) 的情况,设 \(k\) 次洗衣服最大选取的位置是 \(M\) ,则最后的位置期望是 \((M+1)/2\).要求最大位置是 \(M\) 的概率并不好求,然而可以方便地求出最大位置小于 \(M\) 的概率,即 \(p_1 = (\frac{M-1}{n})^k\) ,而最大位置小于 \(M+1\) 的概率为 \(p_2 = (\frac{M+1-1}{n})^k\) , 最大位置等于 \(M\) 的概率就是最大位置小于 \(M+1\) 的概率减去最大位置小于 \(M\) 的概率即 \(p_2-p_1\) .
因此可以按照最大位置来分.当最大位置小于 \(i\) 时,期望位置 \(ExpPosition_M\) 是 \(i\) .当最大位置大于等于 \(i\) 时,期望位置 \(ExpPosition_M\) 是 \((M+1)/2\) ,最大位置为 \(a\) 时,概率 $p_a = (\frac{a+1-1}{n})^k - (\frac{a-1}{n})^k $ .
那么结果就是 \(\sum_{a=1}^{n} p_a *ExpPosition_a\)
时间复杂度 \(\operatorname{O}(n)\)
#include<bits/stdc++.h>
using namespace std;
double getlessp(int M,int n,int k)//calc for k circle, P(MaxPosition < M)
{
return pow((M-1)*1.0/n,k);
}
double getp(int M,int n,int k)//calc for k circle, P(MaxPosition = M)
{
return getlessp(M+1,n,k)-getlessp(M,n,k);
}
signed main()
{
double n,i,k;
cin>>n>>i>>k;
double res = 0;
for(int m = 1;m <= n;m++)
{
double exp_position;
if(m<i)
exp_position = i;
else exp_position = (m+1)/2.0;
res+=exp_position*getp(m,n,k);
}
cout<<fixed<<setprecision(10)<<res;
return 0;
}