abc292[AtCoder Beginner Contest 292] 题解

写点题目转换下心情吧

A-CAPS LOCK

大水题

B-Yellow and Red Card

大水题

C-Four Variables

给定一个数\(N\),问有多少个有序正数数组\((A,B,C,D)\),满足\(A\times B+C\times D=N\)

这题荒芜的大脑拒绝思考,看着复杂度不超,写了\(O(N\sqrt N)\)结束了。思想很简单,就是把\(N\)分成两个正整数\(x,y\),再\(O(\sqrt N)\)找因数,最后把\(x,y\)的因数数量相乘。

知道有\(O(N\log N)\)的算法,但是瞬间能写出来的只有这个简单版的了,要反思()

int n;
const int N = 2e5 + 5;
int a[N];

int getNum(int x){
    if (a[x] != 0) return a[x];
    int num = 0;
    for (int i = 1;i * i <= x; ++i){
        if (x % i == 0){
            int j = x / i;
            num++;
            if (i != j) num++;
        }
    }
    return a[x] = num;
}

int main(void){
    cin >> n;
    ll sm = 0;
    for (int i=1;i<n;++i){
        int t1 = getNum(i);
        int t2 = getNum(n - i);
        sm = sm + 1LL * t1 * t2;
    }
    cout << sm << endl;
    return 0;
}

那现在回忆一下\(O(\log N)\)int getNum(int x)吧,冷静下来想想直接写个类似埃式筛的东西就可以。

void init(){
    for (int i=1;i<=n;++i){
        for (int j=1;1LL * i * j <= n;++j){
            f[i * j]++;
        }
    }
}

D-Unicycliv Components

给定一个\(n\)个结点\(m\)条边的无向图,问是否每个连通块都满足它的点数和边数相同。

嗯……这题一开始看错题目了,然后耽误了很久写的很麻烦。后面发现对不上样例(典)然后发现这题实际的题意挺简单的。正常查连通块有多少点,然后累加它们的度\(d_i\),那么连通块边数就等于\(\frac{\sum d_i}{2}\)。图论稀碎的我也就这点本事了qaq

int n, m;
const int N = 2e5 + 10, M = 2e5 + 10;
int deg[N];
vector<int> ve[N];
int edges = 0, vertices = 0;
bool vis[N];

void dfs(int x){
    int len = ve[x].size();
    vis[x] = true, vertices++, edges += deg[x];
    for (int i : ve[x]){
        if (!vis[i]) dfs(i);
    }
}

int main(void){
    int u, v;
    cin >> n >> m;
    for (int i=1;i<=m;++i){
        cin >> u >> v;
        deg[u]++, deg[v]++;
        ve[u].push_back(v), ve[v].push_back(u);
    }
    for (int i=1;i<=n;++i){
        vertices = edges = 0;
        if (!vis[i]) dfs(i);
        if (vertices << 1 != edges){
            cout << "No" << endl;
            return 0;
        }
    }
    cout << "Yes" << endl;
    return 0;
}

E-Transitivity

给定一个\(n\)个结点\(m\)条边的有向简单图,对于任意一个三元组\((a,b,c)\),可执行操作\(x\):若存在边\(a\rightarrow b\),存在边\(b \rightarrow c\),且不存在边\(a\rightarrow c\),那么就可以手动加上\(a\rightarrow c\)这条边。问最少要加多少条边才能使得无法再执行该操作。

数据范围:\(3\leq N \leq 2000;0\leq M \leq 2000;1\leq u_i,v_i\leq N;u_i\neq v_i;\forall i\neq j,(u_i,v_i)\neq (u_j,v_j)\)

我当时的思路可谓是经典的错误,标准的零分。把整张图分为一个一个连通块,然后一旦出现可以执行操作\(x\)的三元组,就证明整个连通块都可以执行这个操作直至变成完全图。(然后轻易举出了反例:

\(a\rightarrow b\rightarrow c \rightarrow d;e\rightarrow f\rightarrow g\rightarrow d\)。这样的话\(a\)\(g\)之间是不会有有向边的。然后就陷入了“嘶,这样的有向边算不算连通块?”“话说这种东西叫连通块吗,我怎么进一步分割?”等等的错误思想中。实际上再从源头进一步想想,就可以知道这个结论:如果在原图中\(x\)可最终抵达\(y\),那么一定可以添加(或者本来存在)\(x\rightarrow y\)这条边。

本来还在想怎么优化,一抬头看到\(N\times M=4e6\),乐。E题好简单,我真是猪鼻orz

int n, m;
const int N = 2000 + 10;
vector<int> ve[N];
bool vis[N];
int vertices = 0;

void init(){
    for (int i=1;i<=n;++i){
        vis[i] = false;
    }
    vertices = -1;
}

void dfs(int x){
    vis[x] = true, vertices++;
    int xlen = ve[x].size();
    for (int i : ve[x]){
        if (!vis[i]) dfs(i);
    }
}

int main(void){
    cin >> n >> m;
    int u, v;
    for (int i=1;i<=m;++i){
        cin >> u >> v;
        ve[u].push_back(v);
    }
    ll sm = 0;
    for (int i=1;i<=n;++i){
        init();
        dfs(i);
        sm += vertices;
    }
    cout << sm - m << endl;

    return 0;
}

F-Regular Triangle Inside a Rectangle

有一个矩形,边长为正整数\(A,B\)。在矩形里放一个等边三角形,问等边三角形最大的边长是多少。

  • 数据范围:\(1\leq A,B \leq 1000;\)

  • 数据精度要求:\(10^{-9}\)

  • 时间要求:2s

我的小学生数学想法:从长方形的某个顶点出发,印两条夹角为60°的射线,和矩形相交,那么最短的一条交线就是等边三角形的边长。

image-20230309130229057

首先,这个最大的等边三角形,一定会有两个顶点都在矩形边上,不然一定可以找到一个更大的。又大边对大角,小边对小角。\(l_3\)所对的角度是60°,三角形内角和180°,那\(l_1,l_2\)肯定有一条所对的角度小于等于60°,也就是这两条射线的交线中的一条肯定是最小的边长,\(l_3\)不可能是最小的那条边,不需要考虑。

\(l_1=A\times \sec \alpha,l_2=B\times \sec \beta\)

\(\alpha + \beta = 30\)

题目要求精度\(10^{-9}\),然后\(\cos x\)范围是\([0,1]\)嘛,这就30°,分成1e8还不拿下?嗯……然后1e8刚好超时几十ms,然后5e7就有两个点wa了,经过几次尝试发现8e7过了()

然后看了题解之后发现,啊哈哈,对哦,这题可以二分写,然后写了半天二分没弄懂题目什么意思orz,为什么就检查了一个$\sin\theta\leq A/l,\cos \theta \leq B/l,0\leq \theta \leq 30° $啊,至少不是得检查两条边吗qaq(现在也没搞懂题解一到底在写啥)最后含泪写了三分AC了

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pi acos(-1.0)
#define ZERO 1e-15

int A, B;
const int M = 8e7;
double d = 30.0 * pi / 180;

int main(void){

    cin >> A >> B;
    if (A > B) swap(A, B);
    double l = 0, r = d, m1, m2, res = -1;
    while (r - l > ZERO){
        double m1 = l + (r - l) / 3;
        double m2 = l + (r - l) / 3 * 2;

        double len1 = min(A / cos(m1), B / cos(d - m1));
        double len2 = min(A / cos(m2), B / cos(d - m2));
        if (len1 > len2){
            r = m2, res = len1;
        }
        else{
            l = m1, res = len2;
        }
    }

    cout << fixed << setprecision(15) << res << endl;

    return 0;
}

看不懂第一个editorial,好烦躁0.0,直接来数学方法:

假设\(A\)为短边,\(B\)为长边,\(\theta\)\(A\)和三角形边\(l\)的夹角

  • \(A=B\),显然\(\theta=15°\)

  • \(b\)\(a\)很多时,那么这时等边三角形是被短边限制住的,最大的可以画的三角形就是以短边为高的等边三角形。故\(b\)最小也要是\(\frac{A}{\sin 60°}=\frac{2A}{\sqrt 3}\)

    因此,当\(B\geq \frac{2A}{\sqrt 3}\)时,\(\theta=30°\)

  • \(A<B<\frac{2A}{\sqrt 3}\)时,\(l = \frac{B}{\cos(30°-\theta)}=\frac{A}{\cos \theta}\)

化简得\(\tan \theta = \frac{2B}{A} - \sqrt 3\)

\(\sec ^2 \theta = 1 + \tan^2 \theta=4 - \frac{4\sqrt 3B}{A} + \frac{4B^2}{A^2}\)

\(\cos\theta = \frac A {2\sqrt{A^2-\sqrt 3 AB + B^2}}\)

\(l=\frac A {\cos\theta} = 2\sqrt{A^2-\sqrt 3 AB + B^2}\)

代码如下:

int A, B;
double d = pi / 180.0;

int main(void){

    cin >> A >> B;
    if (A > B) swap(A, B);
    double res = -1;
    if (A == B){
        res = A / (cos(15 * d));
    }
    else if (B >= 2.0 * A / sqrt(3)) {
        res = A / (cos(30 * d));
    }
    else{
        res = 2.0 * sqrt(A*A - sqrt(3)*A*B + B*B);
    }

    cout << fixed << setprecision(15) << res << endl;

    return 0;
}
posted @ 2023-04-19 16:31  跳岩  阅读(32)  评论(0编辑  收藏  举报