暑假集训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;
}
posted @ 2023-08-15 20:48  LZH_03  阅读(56)  评论(0编辑  收藏  举报