2016-2017 ACM-ICPC, NEERC, Northern Subregional Contest 组队选拔赛一

Problem C. CodeCoder vs TopForces

题意:

每个人有两个成绩a和b,告诉你n个人的成绩,A能战胜B的条件是A中a或b能够高于B中对应的a或b,或者A不能直接战胜B,但是A能战胜C,但是C能战胜B,那么A也能战胜B。问你每个人都够打败人的数量?

思路:

我们先按照成绩a排序,令mx[i]表示第0,1,2,,,i个人中b成绩的最大值,这样的话,最后一个人能够战胜的数量是已知的,肯定是能打败所有人x个,那么我们就给这个人的b成绩对应的值改为x。

那么我们枚举第i 个人的话,他能够战胜的人数量为max(i, max(1,2,,,mx[i]) ) 用线段树维护一下好了,然后给第i 个人b成绩更新到当前答案。这样不断的查询,不断的更新即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 100000 + 7;

struct Node{
    int a,b;
    int id;
    void read(int i){
        scanf("%d %d",&a, &b);
        id = i;
    }
}p[maxn];

bool cmp(const Node& lhs,const Node& rhs){
    return lhs.a < rhs.a;
}
int mx[maxn], ans[maxn];


int MAX[maxn*10<<2];
int query(int L,int R,int l,int r,int o){
    if (L <= l && r <= R){

        return MAX[o];
    }

    int m = l + r >> 1;
    int ans = 0;
    if (m >= L) ans = max(ans,query(L,R,l,m,o<<1));
    if (m < R) ans = max(ans,query(L,R,m+1,r,o<<1|1));
    return ans;
}


void update(int pos,int v,int l,int r,int o){
    if (l == r){
        MAX[o] = v;
        return;
    }
    int m = l+r>>1;
    if (m >= pos) update(pos,v,l,m,o<<1);
    else update(pos,v,m+1,r,o<<1|1);
    MAX[o] = max(MAX[o<<1],MAX[o<<1|1]);
}

int main(){

    freopen("codecoder.in","r",stdin);
    freopen("codecoder.out","w",stdout);
    int n;
    scanf("%d",&n);
    for (int i = 0; i < n; ++i){
        p[i].read(i);
    }

    sort(p,p+n,cmp);

    mx[0] = p[0].b;
    for (int i = 1; i < n; ++i){
        mx[i] = max(mx[i-1], p[i].b);
    }

    for (int i = n-1; i >= 0; --i){
//        printf
        ans[p[i].id] = max(i, query(1,mx[i],1,1000000,1));
        update(p[i].b, ans[p[i].id], 1, 1000000, 1);
    }

    for (int i = 0; i < n; ++i){
        printf("%d\n",ans[i]);
    }
    return 0;
}


Problem G. Gangsters in Central City

题意:

告诉你一棵树,叶子上的结点称为房子,每个房子都要获得水,只能从根节点(编号为1)流。告诉你q 个操作,每个操作告诉你流氓抓了一个房子,或者离开了一个房子,你不想让流氓得到水,你必须切断一些路,使得流氓都得不到水,但是你非常害怕流氓,你必须切断最少的路c,但是你还要考虑市民,所以,你要让那些得不到水的并且没有流氓的房子的数量h尽量少,每个操作输出c和h。

思路:(LCA+瞎搞)

很抽象的一个题目,读懂了也很乱。

假设根节点有k 个儿子,想一想会发现,你最多切k 个边就可以让所有的房子都得不到水,因此我们只需要考虑根节点的k个儿子形成的路即可。

比如说流氓抓了一个房子v,我们就看这个房子v对应的最上边的是k个边中的哪一个,看看这个有木有被切掉,如果没有被切的话,切掉它,并且统计答案,ans1++。

ans1还是比较简单的。

再来考虑ans2,ans2问你得不到水并且没有流氓的房子尽量少。

因此我们必须找到 我们切掉的是哪一个边,(那k 个边只是为了统计切的数量,但并不能求出切的是哪一条边)。

我们可以把这个树重新编号变成有序的(就是dfs序列),这样的话,我们只需要找到最上边的边中包含结点的最小值x和最大值y,然后算出这俩的LCA,那么这个点就是我们要切的边,因为最小值和最大值就覆盖了整个的包含的结点,我们并不需要计算n 个点的LCA,计算两端的LCA就够了。

切掉后重新统计答案ans2即可。

吐槽:

自己真的眼瞎,自己因为没有看到 房子只会在叶子上,wa了好久。(and the houses are at the leaves)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#define Siz(x) (int)x.size()
using namespace std;

const int maxn = 1e5 + 7;
const int DEG = 20;

int fa[maxn][DEG];
int deg[maxn];


vector<int>g[maxn];
int f[maxn]; /// 结点i的父亲
int siz[maxn]; /// siz[i]表示 以i 为根的这棵子树叶子数量。
int id[maxn];/// 重新编号的id
int fid[maxn];/// id 映射的结点
int cnt;
int father[maxn]; ///映射到最上边的哪一个边
set<int>T[maxn];/// 统计总边包含的结点
int a[maxn]; /// 每一条总边的ans2


void BFS(int root){
    queue<int>que;
    deg[root] = 0;
    fa[root][0] = root;
    que.push(root);
    while(!que.empty()){
        int tmp = que.front(); que.pop();
        for (int i = 1; i < DEG; ++i){
            fa[tmp][i] = fa[fa[tmp][i-1] ][i-1];
        }

        for (int i = 0; i < g[tmp].size(); ++i){
            int v = g[tmp][i];
            if (v == fa[tmp ][0]) continue;
            deg[v] = deg[tmp] + 1;
            fa[v][0] = tmp;
            que.push(v);
        }
    }
}

int LCA(int u,int v){
    if (deg[u] > deg[v]) swap(u,v);
    int hu = deg[u], hv = deg[v];
    int tu = u; int tv = v;
    for (int det = hv-hu, i = 0; det; det>>=1,++i){
        if (det&1) tv = fa[tv][i];
    }
    if (tu == tv) return tu;
    for (int i = DEG-1; i >= 0; --i){

        if (fa[tu][i] == fa[tv][i]) continue;
        tu = fa[tu][i];
        tv = fa[tv][i];
    }
    return fa[tu][0];
}




void dfs(int cur,int pre){
    f[cur] = pre;

    id[cur] = ++cnt;
    fid[cnt] = cur;
    if (Siz(g[cur]) == 0){
        siz[cur] = 1;
        return;
    }
    for (int i = 0; i < Siz(g[cur]); ++i){
        int v = g[cur][i];
        if (cur == 1) father[v] = v;
        else father[v] = father[cur];


//        printf("%d - %d\n",v,father[v]);

        dfs(v,cur);
        siz[cur] += siz[v];
    }

//    printf("%d -- %d\n",cur,siz[cur]);
}


char op[3];
int main(){

    freopen("gangsters.in","r",stdin);
    freopen("gangsters.out","w",stdout);

    int n, q, x;
    scanf("%d %d",&n, &q);
    for (int i = 1; i < n; ++i){
        scanf("%d",&x);
        g[x].push_back(i+1);
    }

    dfs(1,1);
    BFS(1);


    int ans1 = 0, ans2 = 0;
    while(q--){
        scanf("%s%d",op, &x);


        if (op[0] == '+'){
            int fx = father[x];
            if (Siz(T[fx]) == 0){
                ++ans1;
            }

            T[fx].insert(id[x]);

            ans2 -= a[fx];
            int fi = *T[fx].begin();
            int la = *T[fx].rbegin();
            int lca = LCA(fid[fi],fid[la]);
            a[fx] = siz[lca] - Siz(T[fx]);
//            printf("===== %d %d\n",siz[lca]-1, Siz(T[fx]));
            ans2 += a[fx];
        }

        else {
            int fx = father[x];
            if (Siz(T[fx]) == 1){
                --ans1;
            }
            T[fx].erase(id[x]);
            ans2 -= a[fx];

            if (Siz(T[fx]) != 0){
                int fi = *T[fx].begin();
                int la = *T[fx].rbegin();
                int lca = LCA(fid[fi],fid[la]);
                a[fx] = siz[lca] - Siz(T[fx]);
            }
            else {
                a[fx] = 0;
            }
            ans2 += a[fx];
        }

        printf("%d %d\n",ans1,ans2);
    }



    return 0;
}


/**
7 6
1 2 1 3 3 3
+ 4
+ 5
+ 6
+ 7
- 6
- 5


8 100
1 1 1 2 2 5 5

ans :
1 0
2 0
2 1
2 0
2 1
2 0

**/



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值