AtCoder Beginner Contest 226

AtCoder Beginner Contest 226

C - Martial artist

题意:一共有n个功夫,学习一个功夫都需要花费一定的时间,学习第某一个功夫之前都要学习指定的功夫,问学习完第n个功夫至少花费多少时间。

思路:因为想要学会第n个功夫就必须要学习其指定的功夫,可以用dfs来解决,dfs(x)动作代表队含义是学习完第x的功夫需要用多少时间,对于我们已经搜索过的,就标记下,不用多次搜索。

代码:

#include <bits/stdc++.h>
#define pb push_back
#define LL long long
using namespace std;

const int N = 200010;
int n;
int t[N];
vector<int> a[N];
LL ans;
bool vis[N];

void dfs(int x)
{
    vis[x] = true;
    for(auto it : a[x])
    {
        if(!vis[it])
        {
            ans += t[it];
            vis[it] = true;
            dfs(it);
        }
    }
}
int main()
{
    cin >> n;
    for(int i = 1; i <= n; i ++ )
    {
        cin >> t[i];
        int k;
        cin >> k;
        for(int j = 1;j <= k; j ++ )
        {
            int x;
            cin >> x;
            a[i].pb(x);
        }
    }
    dfs(n);
    cout << ans + t[n] << endl;
    // system("pause");
    return 0;
}

D - Teleportation

题意:给你n个点的坐标,你可以定义一个二元组<a, b>,通过它你能从(x, y)传送到(x + a, y + b),可以操作无数次。问你至少需要定义多少二元组,你可以从中选择任意一个二元组任意一次操作,使得你可以任意一个点能到达其他任意一个点。

思路:对于每两个坐标做差并同时除以gcd存到一个集合里面,然后对集合去重,答案就是集合的大小。

#include <bits/stdc++.h>

using namespace std;

int n;
vector<pair<int,int>>p,q;

int main()
{
    scanf("%d",&n);
    for(int i = 1; i <= n; i ++ )
    {
        int x, y;
        scanf("%d %d",&x,&y);
        p.push_back({x,y});
    }
    for(int i = 0; i < n; i ++ )
    {
        for(int j = 0; j < n; j ++ )
        {
            if(i != j)
                q.push_back({p[i].first - p[j].first,p[i].second - p[j].second});
        }
    }
    for(int i = 0; i < q.size(); i ++)
    {
        if(q[i].first == 0){
            if(q[i].second > 0) q[i].second = 1;
            else q[i].second = -1;
        }
        else if(q[i].second == 0){
            if(q[i].first > 0) q[i].first = 1;
            else q[i].first = -1;
        }else{
            int x = abs(__gcd(q[i].first, q[i].second));
            q[i].first /= x, q[i].second /= x;
        }
    }
    sort(q.begin(),q.end());
    q.erase(unique(q.begin(), q.end()),q.end());
    printf("%d\n",q.size());
    return 0;
}

E - Just one

题意:给你N个顶点和M条无向边,你可以随意定义边的方向,使得每一个顶点都只能有一条到达其他顶点的边,问你规划该图的方案数。

思路:本题的结论是对于每一个连通块,如果存在边的数目不等于顶点的数目,答案就是0。否则,答案就是\(2^n\),n为连通块的数目。

代码:

#include <bits/stdc++.h>
#define fi first
#define se second
#define pb push_back

using namespace std;
typedef long long LL;
const int N = 200010;
int n, m;
vector<pair<int,int>>edges;
int cnt1[N], cnt2[N];//分别记录每个联通块点的数目和边的数目
int p[N];

int find(int x)
{
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    scanf("%d %d",&n, &m);
    for(int i = 1; i <= m; i ++ )
    {
        int x, y;
        scanf("%d %d",&x, &y);
        edges.pb({x, y});
    }
    for(int i = 1; i <= n; i ++ ) p[i] = i;
    for(int i = 0; i < m; i ++ )
    {
        int fa = find(edges[i].fi);
        int fb = find(edges[i].se);
        if(fa != fb) p[fa] = fb;
    }
    vector<int>q;//记录连通块
    for(int i = 1; i <= n; i ++ )
    {
        int now = find(i);
        if(!cnt1[now]) q.pb(now);
        cnt1[now] ++;
    }
    for(int i = 0; i < m; i ++ )
    {
        int now = find(edges[i].fi);
        cnt2[now] ++;
    }
    bool flag = false;
    for(auto it : q){
        if(cnt1[it] != cnt2[it]){
            flag = true;
            break;
        }
    }
    if(flag){
        puts("0");
        return 0;
    }
    LL ans = 1;
    for(int i = 1; i <= q.size(); i ++ ) ans = (ans * 1LL * 2) % 998244353;
    printf("%lld\n", ans);
    return 0;
}
posted @ 2021-11-12 12:03  合肥学院王星力  阅读(75)  评论(0)    收藏  举报