*Codeforces Round #202 (Div. 1)*

发烧了,烧了五天,吓死我了,还以为要挂了呢。幸好又活过来了。

A. Mafia

题意:n个人玩一个游戏,每次要选出来一个主持的,剩下n-1个人玩这个游戏。第i个人想玩ai轮游戏,问最少玩多少轮可以满足所有人的要求。

思路:我的思路是二分搜索,先排个序,这样第一个人就是要玩轮数最少的,末尾的就是要玩轮数最多的。对于搜索的每个值,我的策略是先满足要玩轮数最少的那个人,这些轮都是由需要轮数最多的人那个人作为主持人来计算的。需要轮数最少的那个人满足后,然后之后的每轮游戏都有他来主持。然后计算出其余的每个人所玩游戏的轮数,超出他的期望多少或者还差多少轮才能到达他的期望的轮数。然后看看能不能把那些不够的平摊到那些轮数超过的。即看谁玩的轮数过多了,就让他把多出来的轮数不玩了,让他去当主持人。

比如1,10,1000。假设现在搜到的值是1000,那就是第三个人主持1轮,然后第一个人主持999轮,这时候第三个人还差1轮,中间那人已经超出期望990轮了。这就可以把第三个人主持的那一轮分摊到第二个人身上,这样1000就符合条件了。人数多了也是这样。我做完想想我也有点懵,还是看官方题解比较好,感觉那样就很简洁了。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 1e5+10;
LL INF = 2e9+10;
LL a[MAXN],der[MAXN];
int n;

bool C(LL num)
{
    if(num < a[n-1]) return false;
    LL sum = 0;
    for(int i = 1; i < n; ++i)
        sum += (num-a[i]);
    sum -= a[0];
    return sum >= 0;
}

int main()
{
    cin >> n;
    for(int i = 0; i < n; ++i)
        cin >> a[i];
    sort(a,a+n);
    LL l = 0, r = INF,mid;
    while(l <= r)
    {
        mid = (l+r) >> 1;
        if(C(mid)) r = mid - 1;
        else l = mid + 1;
    }
    cout << l << endl;
    return 0;
}
B. Apple Tree

这题最初理解错了,其实是我英语太差。我理解成树的平衡是树每个深度的节点的权值是一样的了。。后来找题解,看到题意才发现理解错了,不过看到题目的真正意思后并没做出来。。。

思路:假设现在树上总共有sum个苹果,我们计算出符合题目要求的苹果数在不超过sum的情况下,最多为num,则sum-num就是结果。根据题意,我们可以计算出每个叶子节点应该在整棵树中占多大的比重。比如有一个根结点,他有三个儿子节点,则每个儿子节点占比重为1/3,知道每个叶子节点占的比重后,就可以根据这个比例计算出整棵树应该有多少苹果。每个叶子算出一个值,选取最小的,因为只能移除苹果,所以要满足最小的才行,毕竟不能向上边添加苹果。这样就找到了满足某个叶节点的最多的苹果数量,假设这个数量是num。因为每个叶节点的苹果数量都可以整除整棵树的苹果数量,即他们是整棵树数量的1/x。所以整棵苹果树的数量还要能够整除这些所有的x的lcm。即苹果的数量是num-num%lcm。所以要移除的苹果数量就是sum-(num-num%lcm)。当lcm太大,超过num的时候,就把整棵树的苹果都拿走。

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int MAXN = 1e5+10;
const LL INF = 0x3f3f3f3f3f3f3f3f;
int n;
vector<int> G[MAXN];
LL a[MAXN],sum,full = INF,lcm = 1;
bool flag = false;

LL Lcm(LL a, LL b)
{
    return a/__gcd(a,b)*b;
}

void dfs(int u, int fa, int mul)
{
    if(flag) return;

    if(mul > full)
    {
        flag = true;
        return;
    }

    if(G[u].size() == 1 && fa != -1)
    {
        full = min(full,a[u]*mul);
        lcm = Lcm(lcm,mul);
        return;
    }

    for(int i = 0; i < G[u].size(); ++i)
    {
        int v = G[u][i];
        if(v == fa) continue;
        dfs(v, u, mul*(fa == -1 ? G[u].size():G[u].size()-1));
    }
}

int main()
{
    int u,v;
    cin >> n;
    for(int i = 1; i <= n; ++i)
    {
        cin >> a[i];
        sum += a[i];
    }
    for(int i = 1; i < n; ++i)
    {
        cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1,-1,1);
    if(flag) cout << sum << endl;
    else cout << sum - full + full%lcm<< endl;
    return 0;
}
C. Subset Sums

不会做,我只会粗鲁的暴力,不会这么优雅的暴力。
我厚颜无耻的搬运了下代码,就不多说了,理解都在注释里。
参考:http://blog.csdn.net/v5zsq/article/details/76047844

http://blog.csdn.net/FromATP/article/details/68927284

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1e5+10;
typedef long long LL;
int n,m,q,block;
LL a[MAXN];
bool h[MAXN];//h[i]=true表示表示i是重集合,否则为轻集合
int Cnt[MAXN][365];//cnt[i][j]表示集合i和集合j相交元素的个数
int id[MAXN],cnt;//cnt是重集合个数,id[i]记录第i个重集合是哪个集合
LL sum[MAXN];//重集合的元素所指数字的和
LL add[MAXN];//
vector<int> g[MAXN],gg[MAXN];//g记录每个集合的元素,gg记录某个元素属于哪几个重集合

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m >> q;
    for(int i = 1; i <= n; ++i)
        cin >> a[i];
    block = sqrt(n);//轻重集合块数划分
    cnt = 0;
    int num,temp;
    for(int i = 1; i <= m; ++i)
    {
        cin >> num;
        //判断轻重集合
        if(num >= block) h[i] = true, id[++cnt] = i;
        else h[i] = false;

        for(int j = 0; j < num; ++j)
        {
            cin >> temp;
            g[i].push_back(temp);
            //计算重集合的和,记录每个元素属于哪些重集合
            if(h[i]) sum[i] += a[temp], gg[temp].push_back(cnt);
        }
    }
    //m个集合
    //计算每个集合和每个重集合的交集
    for(int i = 1; i <= m; ++i)
    {
        for(int j = 0; j < g[i].size(); ++j)
        {
            int u = g[i][j];//u所在的集合为i
            for(int k = 0; k < gg[u].size(); ++k)//u所在的重集合
            {
                int v = gg[u][k];
                Cnt[i][v]++;//i和u所在的重集合有交集
            }
        }
    }

    while(q--)
    {
        int k,x;
        char op;
        cin >> op >> k;
        if(op == '?')
        {
            LL res = 0;
            if(h[k])//重集合通过标记计算
            {
                res = sum[k];
                for(int i = 1; i <= cnt; ++i)
                    res += add[id[i]]*Cnt[k][i];
            }
            else//轻集合直接计算
            {
                //直接计算轻集合
                for(int i = 0; i < g[k].size(); ++i)
                    res += a[g[k][i]];
                //计算上重集合的影响
                for(int i = 1; i <= cnt; ++i)
                    res += add[id[i]]*Cnt[k][i];
            }
            cout << res << endl;
        }
        else
        {
            cin >> x;
            if(h[k]) add[k] += x;//重集合直接标记加了x
            else//轻集合直接更新
            {
                //直接更新a数组
                for(int i = 0; i < g[k].size(); ++i)
                    a[g[k][i]] += x;
                //顺路更新对每个重集合的影响
                for(int i = 1; i <= cnt; ++i)
                    sum[id[i]] += x*Cnt[k][i];
            }
        }
    }

    return 0;
}
D. Turtles

……

E. Pilgrims

……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值