2021“MINIEYE杯”中國大學生算法設計超級聯賽(7)
1003. Fall with Trees
- 題意
一棵完全二叉樹,且子節點和父節點高度一定,同一深度的子節點y坐標相同,且每兩個子節點的距離相同, 求整棵樹連起來的總面積。
- 思路
建議推公式求,暴力調精度會出大問題(
嗚嗚嗚)。
首先\(S_i =h * \frac{w_k + w_{k + 1} }{2}\)
然后每個\(w_1 = fabs(x_2 - x_1), h = y_1 - y_0\),\(w_i = 2 * w_1 - 2^{1-i}\)
然后推出公式展開求一個等比前\(n\)項就可以了。
\(ANS = \sum_{i=1}^{k-1} S_i = w_1 * h / 2.0 * (4 * (k - 1) - 3 * (2 - 2^{k-2})\)
code :
void solve(){
db x0,x1,x2,y0,y1,y2;
int k;
sc("%d", &k);
scanf("%lf%lf%lf%lf%lf%lf", &x0, &y0, &x1, &y1, &x2, &y2);
db h = fabs(y1 - y0);
db w = fabs(x2 - x1);
db ans = w * h / 2.0 * (4.0 * (k - 1) - 3.0 * (2.0 - pow(2,2 - k)));
pr("%.3lf\n", ans);
}
1004. Link with Balls
- 題意
給你一個\(n\),\(m\),有\(2n\)個筐,你可以從\(2x - 1\)的筐中拿\(k*x\)個球(k為非負整數),也可以從\(2x\)的框中最多拿\(x\)個球, 問如果要\(m\)個球,有多少種拿法。
- 思路
1 2 3 4 5 6 \(+\infty\) 0 ~ 1 2k 0 ~ 2 3k 0 ~ 3 我們可以發現,第一個為正無窮,然后其實將\(2\ and\ 3\)組合,也可以得到正無窮。
最后我們就得到\(n\)個無窮筐和一個從\(0 \to n\)的筐。
然后就枚舉最后那個筐選中的個數,然后在剩下的在前面的無窮筐中選即可
然后就是隔板法取即可,在\((m - i)\)個數中,用\(n - 1\)個隔板划分成\(n\)個區域
計算\(\sum_{i = 0}^nC_{m - i + n - 1}^{n-1}\)
由公式\(\sum_{i=0}^nC_{k+i}^k = C_{n + k + 1}^{k + 1}\)
即\(ans = C_{n + m}^n - C_{m - 1}^n\)
code :
ll fact[N], infact[N];
void init() {
fact[0] = infact[0] = 1;
for (int i = 1 ; i < N; ++i) {
fact[i] = fact[i - 1] * i % MOD;
infact[i] = infact[i - 1] * pow_mod(i, MOD - 2) % MOD;
}
}
ll C(ll a,ll b) {
if(a<0||b<0||a<b) return 0;
return fact[a] * infact[a - b] % MOD * infact[b] % MOD;
}
ll qmi(int a, int k, int p) {
LL res = 1;
while (k) {
if (k & 1) res = (LL)res * a % p;
k >>= 1;
a = (LL)a * a % p;
}
return res;
}
void solve(){
int n,m;
cin >> n >> m;
ll ans = C(n + m,n) - C(m - 1,n) + 3 * mod;
cout << ans % mod << endl;
}
1006. Link with Grenade
- 題意
一個人,往任意方向扔一顆速度為\(v\),爆炸范圍為\(r\)的手榴彈,並且將在\(t\)秒后爆炸,問不炸死自己的概率。
- 思路
畫圖形,推一遍物理公式即可
code : (略)(過於難看)
1007. Link with Limit
- 題意
定義一個函數\(f(x)\), 且\(x\)和\(f(x)\)都屬於\([1,n]\) , 且\(f_n(x) = f(f_{n-1}(x))\ and \ f_1(x) = f(x)\)
定義\(x\)的權力為
\(g(x) = lim_{n \to +\infty} \frac{1}{n} \sum_{i=1}^{n}f_i(x)\)
問是否所有\(x\)的權利都相同 (\(1 <= x <= n\))
- 思路
題意其實看懂就知道當\(n \to +\infty\)時,非環點的權值可以忽略因為一定要\(* \frac{1}{+\infty}\), 然后其實就是權值就是一堆環內一直跳的點,那么拓撲排序清邊,然后一直找環即可,判斷是否每個環的點權平均值都相同即可。
code:
int a[N], ru[N];
int q[N];
void solve(){
int n;
cin >> n;
memset(ru, 0, sizeof ru);
fep(i,1,n) {
cin >> a[i];
ru[a[i]] ++;
}
int hh = 0,tt = -1;
fep(i,1,n) {
if(ru[i] == 0) q[++ tt] = i;
}
while(hh <= tt) {
int t = q[hh ++];
ru[a[t]] --;
if(ru[a[t]] == 0) {
q[++ tt] = a[t];
}
}
ll va = -1, vb = -1;
for(int i = 1;i <= n;i ++) {
ll sa = 0,sb = 0;
if(ru[i]) {
int j = i;
while(ru[j]) {
ru[j] = 0; // 每個點只會出現在一個環上
sa ++;
sb += j;
j = a[j];
}
if(va == -1) {
va = sa, vb = sb;
}else {
if(va * sb != vb * sa) {
cout << "NO" << endl;
return;
}
}
}
}
cout << "YES" << endl;
}
1008. Smzzl with Greedy Snake
- 題意
貪吃蛇,可以直走 = \(f\),順時針旋轉\(90^{\circ}\) = \(c\), 逆時針\(90^{\circ}\) = \(u\), 每次吃完一個才會出現下一個,問你如何用最少的行動吃完。
- 思路
模擬0.0
code:
void solve(){
int x,y,d;
cin >> x >> y >> d; // 0 - y+, 1- x+, 2- y-,3 x-
int m;
cin >> m;
string str = "";
fep(i,1,m) {
int a,b;
cin >> a >> b;
int flag = -1; // 0 - 左上, 1- 左下,2- 右上, 3- 右下
// 先判斷直走
if(a > x) flag += 2;
if(b < y) flag += 2;
else flag ++;
if(flag == 0) {
if(d == 1) d = 0, str += "u";
if(d == 2) d = 3, str += "c";
if(d == 0) {
while(y < b) str += "f", y ++;
str += "u";
while(x > a) str += "f", x --;
d = 3;
}
else if(d == 3) {
while(x > a) str += "f", x --;
str += "c";
while(y < b) str += "f", y ++;
d = 0;
}
}else
if(flag == 1) {
if(d == 0) d = 3, str += "u";
if(d == 1) d = 2, str += "c";
if(d == 2) {
while(y > b) str += "f", y --;
str += "c";
while(x > a) str += "f", x --;
d = 3;
}
else if(d == 3) {
while(x > a) str += "f", x --;
str += "u";
while(y > b) str += "f", y --;
d = 2;
}
}else
if(flag == 2) {
if(d == 2) d = 1, str += "u";
if(d == 3) d = 0, str += "c";
if(d == 0) {
while(y < b) str += "f", y ++;
str += "c";
while(x < a) str += "f", x ++;
d = 1;
}
else if(d == 1) {
while(x < a) str += "f", x ++;
str += "u";
while(y < b) str += "f", y ++;
d = 0;
}
}else {
if(d == 3) d = 2, str += "u";
if(d == 0) d = 1, str += "c";
if(d == 1) {
while(x < a) str += "f", x ++;
str += "c";
while(y > b) str += "f", y --;
d = 2;
}
else if(d == 2) {
while(y > b) str += "f", y --;
str += "u";
while(x < a) str += "f", x ++;
d = 1;
}
}
}
cout << str << endl;
}
1010. Smzzl with Tropical Taste
- 題意
一個泳池,一個人每秒忘泳池里倒\(q\)升的冰紅茶,一個人每秒從泳池里喝\(p\)升的冰紅茶(
手動滑稽),問對任意\(G\)升冰紅茶,都存在一個時間\(T\),在男孩和了\(t\)秒后的,喝了大於\(G\)升的冰紅茶。
- 思路
只有當倒的冰紅茶小於喝的時候才會有限制,其他時候都是無限制的。
code :
const double eps = 1e-6;
void solve(){
db n,m;
cin >> n >> m;
if(n - m > eps) {
cout << "ENJ0Y YOURS3LF!" << endl;
}else {
cout << "N0 M0R3 BL4CK 1CE TEA!" << endl;
}
}
1012. Yiwen with Sqc
- 題意
給一個字符串,\(sqc(s,l,r,c)\)表示字符串\(s\)中從\(l \to r\)中\(c\)字符出現的次數,然后求一個:
\(\sum_{c=97}^{122}\sum_{i=1}^{n}\sum_{j=i}^{n} sqc(s,i,j,c)^2 mod 998244353\)
- 思路
我的解法是推公式寫的。。
就比如: ababa
1: a a貢獻 a : 1
2: b ab貢獻a: 1,b : 1 + 1
3: a aba貢獻a: 4 + 1 + 1, b : 1 + 1
4: b abab貢獻a:4 + 1 + 1, b : 4 + 4 + 1 + 1
5: a ababa貢獻a: 9 + 4 + 4 + 1 + 1, b : 4 + 4 + 1 + 1.
然后發現其他字符變動對自己的貢獻是沒有影響的。
之后就靠腦子想了,
\(a\)中的貢獻為\(a_1 + a_3 + a_5\),
當\(a_1\)進來時為1,
\(a_3\)進來后即為\((a_1=1 + 1)^2 + 1 + k_i\),\(k_i\)表示有多少次沒有出現當前字母了,表示\(k_i\)個\(+1\),
然后\(a_5\)進來,就是\((a_1=1 + 1 + 1)^2) + (a_2=1 + 1)^2 + (a_2=1 + 1)^2 + 1 + k_i = 9 + 4 + 4 + 1 + 1\), 然后計算公式\(O(26n)\)即可求解,沒有卡常。。
code:
char str[N];
int f[N][27];
int s[N][27];
int cnt[27], k[27]; // cnt表示當前某個字符貢獻了多少次如+4+1+1,貢獻為6,cnt為3, k表示已經多久沒出現當前字符
void solve(){
sc("%s", str + 1);
int n = strlen(str + 1);
for(int i = 1;i <= n;i ++) {
fep(j,1,26) f[i][j] = f[i - 1][j], s[i][j] = s[i - 1][j], k[j] ++;
if(!cnt[str[i] - 'a' + 1]){
int now = str[i] - 'a' + 1;
f[i][now] = i;
s[i][now] += cnt[now] + k[now];
cnt[now] += k[now];
k[now] = 0;
}
else {
int now = str[i] - 'a' + 1;
f[i][now] += cnt[now] + k[now] + 2 * s[i][now] % mod;
f[i][now] %= mod;
cnt[now] += k[now];
s[i][now] += cnt[now];
k[now] = 0;
}
}
int ans = 0;
for(int i = 1;i <= n;i ++) {
fep(j,1,26){
ans += f[i][j];
ans %= mod;
}
}
fep(i,1,26) k[i] = cnt[i] = 0;
pr("%lld\n", ans);
}