G、 Lexicographical Maximum
题目大意:
- 给定一个n(1 ≤ n ≤ 10 ^ 1e6)
- 输入1 ~ n按字典序排列的最大的数
解法:
- 首先把n当作字符串对待
- 如果len(n) == 1,那么答案就是n
- 如果len(n) >= 2,如果答案不是n的话,那么答案一定是len(n) - 1个9。如果答案是n的话,那么n一定前len(n) - 1个是9。那么就只需要判断n的前len(n) - 1个是不是9。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s;
cin >> s;
if(s.size() == 1) cout << s << endl;
else
{
int flag = 0;
for(int i = 0;i < s.size() - 1;i ++)
if(s[i] != '9')
{
flag = 1;
break;
}
if(!flag) cout << s << endl;
else
{
for(int i = 1;i < s.size();i ++) cout << "9";
cout << endl;
}
}
return 0;
}
A、 Villages: Landlines
题目大意:
- 现有一根数轴,在上面有n个点,其中1个为发电站,剩下n - 1个为建筑物
- 任务是通过在数轴上放电力塔,使得所有发电站和建筑物相连通
- 发电站和建筑物之间不能直接连通,必须通过电力塔作为连通介质
- 发电站和建筑物都具有一个半径属性,代表和它距离小于这个半径的电力塔,可以直接相连不需要电线,不然就需要电线。
- 求至少需要多少电线,可以使得所有建筑相连
解法:
- 把发电站和建筑物抽象成一个个的圆,就形成了如图的形状:
其中红色为发电站,蓝色为建筑物,可以发现,如果建筑物和发电站有交点,那么在交点处设立一个电力塔,就可以使得2者相连,并且无需电线。但是如果无交点的话,就必须在中间设立一个电力塔,把他们相连起来,需要的电线长度就是中间的那段距离。然后建筑物之间,如果有交点就在交点处设立,否则也就是需要电线。建筑物到发电站可以通过很多个电力塔到达。 - 那么问题再简化,就是有很多个区间,有交点的区间可以进行合并。最后求剩下区间之间的距离之和就是答案。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int, int> pii;
bool cmp(pii a, pii b)
{
if(a.first == b.first)
return a.second < b.second;
return a.first < b.first;
}
int main()
{
vector<pii> q;
int n;
cin >> n;
for(int i = 1;i <= n;i ++)
{
int x, d;
cin >> x >> d;
q.push_back({x - d, x + d});
}
sort(q.begin(), q.end(), cmp);
vector<pii> p;
int l = q[0].first, r = q[0].second;
for(int i = 1;i < q.size();i ++)
{
if(q[i].first <= r)
{
r = max(r, q[i].second);
}
else
{
p.push_back({l, r});
l = q[i].first, r = q[i].second;
}
if(i == q.size() - 1)
p.push_back({l, r});
}
int res = 0;
for(int i = 1;i < p.size();i ++) res += (p[i].first - p[i - 1].second);
cout << res << endl;
return 0;
}
D、Mocha and Railgun
题目大意:
- 给定一个圆心为原点的圆,以及一个在圆内的点Q
- 并且指定圆的半径r以及一个整数d
- 可以首先指定一个角度,然后形成一个角度为α,中点为Q,长度为2d的线段,然后把这个线段投影到圆边上形成一个弧。
- 可以任意选择角度,问这个弧最长可以是多少
解法:
- 对于给定的参数,任选一个角度然后投影的弧如图:
- 然后旋转坐标系可以变成下图:
- 可以发现,对于任意角度形成的线段,都可以通过Q点以原点为中心进行旋转,然后使得线段与x轴平行,并且投影的弧长不变。
- 当线段与x轴平行之后,就可以直接用线段在x轴上的投影来确定弧长了
- 而不同角度,通过旋转然后投影到x轴上的区别就在于Q点在x轴上的投影
- 连接原点和Q点,假设这个距离为dist,可以发现,Q点在旋转过程中投影在x轴上的点坐标范围为(-dist, 0) ~ (dist, 0),那么问题就变成了在这个范围内选择一个点,然后以这个点为中心形成一个长度为2d的线段,向上投影,然后求出最长的弧长。
- 假设现在取点坐标为(x, 0),那么它对应的弧长如图:
- 想要弧长越长,那么就必须A点和B点的距离越远
A = ( x − d , r 2 − ( x − d ) 2 ) A = (x - d, \sqrt{r^2-(x-d)^2}) A=(x−d,r2−(x−d)2)
B = ( x + d , r 2 − ( x + d ) 2 ) B= (x + d, \sqrt{r^2-(x+d)^2}) B=(x+d,r2−(x+d)2)
d i s t ( A B ) = 4 d 2 + ( r 2 − ( x − d ) 2 − r 2 − ( x + d ) 2 ) 2 dist(AB) = \sqrt{4d^2+( \sqrt{r^2-(x-d)^2}- \sqrt{r^2-(x+d)^2})^2} dist(AB)=4d2+(r2−(x−d)2−r2−(x+d)2)2
其中d是固定的,并且根号是单调递增函数,所以式子可以简化成
r 2 − ( x − d ) 2 − r 2 + ( x + d ) 2 r^2-(x-d)^2-r^2+(x+d)^2 r2−(x−d)2−r2+(x+d)2
− x 2 + 2 d x − d 2 + x 2 + 2 d x + d 2 = 4 d x -x^2+2dx-d^2+x^2+2dx+d^2=4dx −x2+2dx−d2+x2+2dx+d2=4dx
所以最后只需4dx很大,AB的距离就可以很大,那么x就取最大的正数
x = d i s t x=dist x=dist
x确定之后,就变成了下图:
那么现在的问题就变成了求α和β的角度,这里利用反三角函数就可以解出。
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int main()
{
int t;
scanf("%d", &t);
while(t --)
{
double r, x, y, d;
scanf("%lf %lf %lf %lf", &r, &x, &y, &d);
double dist = sqrt(x * x + y * y);
double d1 = acos((dist - d) / r);
double d2 = acos((dist + d) / r);
printf("%.12lf\n", r * (d1 - d2));
}
}
I、 Chiitoitsu
题目大意:
- 定义一个麻将规则,这个麻将具有34种不同类型的牌,每一种类型的牌具有4张
- 首先会拿到其中的13张牌,相同牌至多出现2张,然后每一轮操作,会在剩下的牌中摸一张,然后选择要或者不要。要的话就还需要打出手中13张牌中的一张,不要的话这张牌废弃,并且不会再被摸到
- 如果在执行操作的时候,摸到一张牌和手中的13张牌组成了7个对子,那么就糊了
- 给定初始手牌,求出采取最优策略的情况下达到七对子的期望轮数
解法:
-
首先求出最优策略,题意说相同牌至多出现2张,那么也就代表一开始只有单牌或者对子,设单牌为x,对子为y,于是就有
x = 13 − 2 y ( 0 ≤ y ≤ 6 ) x=13-2y(0\le y\le 6) x=13−2y(0≤y≤6) -
那么单牌的数量一定是奇数,所以最优策略就是摸到一张牌,能凑出对子就要并且丢单牌,不能就不要。
-
然后期望轮数的公式是:
E ( X ) = ∑ x P ( x ) ( 1 ≤ x ≤ 121 ) E(X)=\sum xP(x) (1\le x\le 121) E(X)=∑xP(x)(1≤x≤121)
x的值代表轮数,P(x)为第x轮出七对子的概率
13张牌肯定无七对子,所以 x ≥ 1 x\ge 1 x≥1
发完牌之后还剩 34 × 4 − 13 = 123 34\times4-13=123 34×4−13=123张牌,在最坏情况下就是摸完了其他所有牌(120张),到最后才从3张一样的牌中摸到一张组成七对子,所以 x ≤ 121 x \le 121 x≤121 -
那么问题的主要就是求这个概率,第x轮出七对子的概率,换句话也就是说第x轮把所有单牌去掉的概率
-
因为最优策略采取的方法,每一次选择都只需要关注当前手牌的情况,所以就可以使用DP求解了
-
设 d p [ i ] [ j ] dp[i][j] dp[i][j]代表第 i 轮把 j 张单牌去掉的概率
状态转移方程为:
d p [ i ] [ j ] = d p [ i − 1 ] [ j − 2 ] × 3 × ( k − j + 2 ) 124 − i + d p [ i − 1 ] [ j ] × 124 − i − 3 × ( k − j ) 124 − i dp[i][j]=dp[i-1][j-2]\times \frac{3\times (k-j+2)}{124-i}+dp[i-1][j]\times \frac{124-i-3\times (k-j)}{124-i} dp[i][j]=dp[i−1][j−2]×124−i3×(k−j+2)+dp[i−1][j]×124−i124−i−3×(k−j)
特殊情况:
当 i = 0 , j = 0 i=0, j=0 i=0,j=0的时候, d p [ i ] [ j ] = 1 dp[i][j]=1 dp[i][j]=1,代表初始局面去掉0张单牌的概率为1
当 i ≥ 1 , j = 0 i\ge 1, j=0 i≥1,j=0的时候, d p [ i ] [ j ] = d p [ i − 1 ] [ j ] × 124 − i − 3 × ( k − j ) 124 − i dp[i][j]=dp[i-1][j]\times \frac{124-i-3\times (k-j)}{124-i} dp[i][j]=dp[i−1][j]×124−i124−i−3×(k−j)
公式中的 k = k= k= 初始局面单牌的数量 -
通过dp求出来概率之后,就可以直接利用概率算出期望了(题中是对最后的答案 % 1e9+7,所以还需要求逆元)
#include <iostream>
#include <map>
#include <string>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 130, M = 20, mod = 1e9 + 7;
ll f[N][M], res[M];
ll qmi(ll a,ll b)
{
ll res = 1;
while(b)
{
if(b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
void init()
{
for(int k = 1;k <= 13;k += 2)
{
memset(f, 0, sizeof f);
f[0][0] = 1;
for(ll i = 1;i <= 121;i ++)
for(ll j = 0;j < k;j += 2)
{
f[i][j] = f[i - 1][j] * (124 - i - 3 * (k - j) % mod) % mod * qmi(124 - i, mod - 2) % mod;
if(j)
f[i][j] = (f[i][j] + f[i - 1][j - 2] * 3 % mod * (k - j + 2) % mod * qmi(124 - i, mod - 2) % mod) % mod;
}
for(ll i = 1;i <= 121;i ++)
res[k] = (res[k] + i * f[i - 1][k - 1] % mod * 3 % mod * qmi(124 - i, mod - 2) % mod) % mod;
}
}
int main()
{
ios::sync_with_stdio(false);
init();
int t;
cin >> t;
for(int d = 1;d <= t;d ++)
{
string s;
cin >> s;
map<string, int> mp;
for(int i = 0;i < s.size();i += 2)
{
string temp = s.substr(i, 2);
mp[temp] += 1;
}
int cnt = 0;
for(auto x:mp) if(x.second == 1) cnt += 1;
cout << "Case #" << d << ": " << res[cnt] << endl;
}
return 0;
}