COCI2014/2015CONTEST #3 honi&stogovi(LCA)

来源:互联网 发布:各行业数据查询 编辑:程序博客网 时间:2024/05/18 01:22

第四题HONI:

给出n位选手,以及每位选手两场比赛的得分。如果一位选手的两场比赛都严格大于另一位选手的两场比赛的得分,那么他第三场比赛成绩也严格大于那位选手的得分。如果两位选手的比赛总分相等,排名不分先后。求n位选手可能的排名。

模拟赛的时候实在想得太多太多了,居然用了拓扑排序- -,然后暴搜方案数;最后想到了严格大于,结果没有想到区间求和委屈,实在遗憾啊!

正解是统计二维区间前缀和。最先用树状数组写了一遍,结果超时,后来再看看数据范围,每一场比赛的分数不超过650,于是直接统计前缀和。如果对于选手i,第一场分数记为a[i],第二场分数记为b[i],分数比他严格小的人有x人,分数比他严格大的有y人,那么他最坏的排名不会超过n-x,最好的排名不会优于y+1,于是他的排名区间就是[y+1,n-x],所以对于每一个人,我们在标记表格k[a[i]][b[i]]的值加1,代表这个分数的人数,然后统计k数组的前缀和,得到的sum[p][q]就是第一场分数小于等于p,第二场分数小于等于q的总人数。

写到这里之后,我还是WA了,

因为,特别要注意一点的是,如果第i位选手,他的第一场分数a[i]=650(最高分),那么他的排名可能与其他第一场比赛为0分,第二场比赛为b[i]分的人并列:做最坏的打算,第i位选手第三场比赛为0分,另外的选手第三场比赛650分。我们在统计严格小于的排名的时候是考虑不到这样的情况的!

比如:有两位选手,第一位选手分数为650 1 第二位选手分数为0 1,那么第一位选手的排名就是[1,1],第二位选手的排名就是[1,2]。(理解出现障碍了,对吧!)

#include<cstdio>#define MAXN 650using namespace std;int n,a[500010][2];int k[MAXN+5][MAXN+5];int sum[MAXN+5][MAXN+5];int main(){    scanf("%d",&n);    for(int i = 1; i <= n; i++)    {        scanf("%d%d",&a[i][0],&a[i][1]);k[a[i][0]][a[i][1]]++;    }for(int i = 0; i <= MAXN; i++){for(int j = 0; j <= MAXN; j++){sum[i][j] = k[i][j];if(i > 0) sum[i][j] += sum[i-1][j];if(j > 0) sum[i][j] += sum[i][j-1];if(i > 0&&j > 0) sum[i][j] -= sum[i-1][j-1];}}    for(int i = 1; i <= n; i++)    {        int t1,t2;t1 = sum[a[i][0]-1][a[i][1]-1] + k[a[i][0]][0]*(a[i][1] == MAXN) + k[0][a[i][1]]*(a[i][0] == MAXN);//严格小于,当有一场的分数为650的最高分时//另外一场分数为b[i]另一场为0分的人永远也不可能超过他t2 = sum[650][650] - sum[650][a[i][1]] - sum[a[i][0]][650] + sum[a[i][0]][a[i][1]];//严格大于printf("%d %d\n",t2+1,n-t1);    }}

第五题STOGOVI:

Mirko在玩堆栈游戏。开始他有一个空的堆栈,编号为0.在第i步(1<=i<=300000),他会选择一个编号为v的堆栈,复制一份并做如下操作:
1.a v 表示将v号堆栈复制一份,新栈的编号为i,并将元素i压入新栈的栈顶。
2. b v 表示将v号堆栈复制一份,新栈的编号为i,将新栈的栈顶元素弹出。
3.c v w 将v号堆栈复制一份,编号为i,并比较第v号和第w号堆栈中有多少相同的数。

模拟赛的时候抽风,直接写了vector来模拟,然后各种爆内存超时。

其实仔细看一下就会发现,这就是一道在线的LCA的题目。对于a操作,就是新增一个节点i,他是v的儿子节点;对于操作b,就是求节点v属于哪个节点的集合,然后把节点i放入节点v的父亲节点集合;然后操作c就是把节点i放入节点v的集合,然后求v,w最近公共祖先的深度。

#include<cstdio>#include<iostream>#include<cstring>#define MAXN 300010using namespace std;int belong[MAXN];//相当于是一个并查集int p[MAXN][20];int dep[MAXN];int n,v,w;char ops[3];int lca(int a,int b){    int i,j;    if(dep[a] < dep[b]) swap(a,b);    for(i = 0; (1<<i) <= dep[a]; i++);    i--;    for(j = i; j >= 0; j--)        if(dep[a] - (1<<j) >= dep[b])            a = p[a][j];    if(a == b) return a;    for(j = i; j >= 0; j--)        if(p[a][j] != -1&&p[a][j] != p[b][j])        {            a = p[a][j];            b = p[b][j];        }    return p[a][0];}int main(){    memset(p,-1,sizeof p);    scanf("%d",&n);    for(int i = 1; i <= n; i++)    {        scanf("%s%d",ops,&v);        v = belong[v];                 if(ops[0] == 'a')//增加新的节点        {            belong[i] = i;            dep[i] = dep[v] + 1;            p[i][0] = v;            for(int j = 1; j <= 20; j++)//宁可多循环几次,最先按照模板写成了(1<<j)<=20                if(p[i][j-1] != -1) p[i][j] = p[p[i][j-1]][j-1];        }        else if(ops[0] == 'b')//复制并删除,即该栈并入他的父亲的集合        {            printf("%d\n",v);            belong[i] = p[v][0];        }        else //找出两点的最近公共祖先,并求深度        {            scanf("%d",&w);            w = belong[w];            belong[i] = v;            printf("%d\n",dep[lca(v,w)]);        }    }}


0 0
原创粉丝点击