2021“MINIEYE杯”中国大学生算法设计超级联赛(7)

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);
}

  • 题意

给你一个\(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;
}
  • 题意

一个人,往任意方向扔一颗速度为\(v\),爆炸范围为\(r\)的手榴弹,并且将在\(t\)秒后爆炸,问不炸死自己的概率。

  • 思路

画图形,推一遍物理公式即可

code : (略)(过于难看)

  • 题意

定义一个函数\(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);
}
posted @ 2021-08-11 10:51  darker_wxl  阅读(221)  评论(3编辑  收藏  举报
window.onload = function(){ $("#live2dcanvas").attr("style","position: fixed; opacity: 0.7; left: 70px; bottom: 0px; z-index: 1; pointer-events: none;") }