gym102500 A. Average Rank

题意:

n个人进行w次比赛,每次比赛有若干胜者(给定),胜者的积分+1。每次赛后重新计算每个人的排名(当前积分比他严格大的人数,可以并列)。w次比赛后,分别求每个人的排名的平均值。

\(1\le n,w \le 3e5\)

思路:

x 获胜后,原来 x 同分的人的排名都+1。如果每次赛后都更新所有人的积分肯定超时。应该更新积分的信息。

p[x] 表示 x 的积分,ans[x] 表示 x 的排名和,rk[p] 表示积分 p 的排名

sum[p] 表示积分 p 的前缀贡献,即如果一个人的积分从头到尾一直都是 p,他的排名和是多少。

last[p] 表示上次计算 sum[p] 的时间(“结算”)

pre[x] 表示 x 刚变成积分 px 的时候,px 的贡献 sum[px]

x 获胜后,x 的原积分 px 的排名+1。这时更新一下从上次更新的时刻 last[px] 到现在,sum[px] 增加了多少。那么 x 的排名和要加上 x 在 px 积分逗留期间涨了多少排名和,即 ans[x] += sum[px] - pre[x]

然后 x 进入新的积分,px++。要记录一下 x 到新的积分 px 时,sum[px] 是多少,为此,需要先算一下最新的 sum[px]

最后把没算完的算一下。要注意上述排名 rk 是从 rk=0 算起的,每次比赛都少算了一名,最后要 +1

ll p[N], ans[N], pre[N];
ll rk[N], last[N], sum[N];
signed main() {
    iofast;
    int n, w; cin >> n >> w;
    for(int i = 0; i < w; i++) { //从0开始
        int k; cin >> k; while(k--) {
            int x; cin >> x;

            sum[p[x]] += rk[p[x]] * (i - last[p[x]]);
            last[p[x]] = i;
            ans[x] += sum[p[x]] - pre[x];
            ++rk[p[x]];

            ++p[x];

            sum[p[x]] += rk[p[x]] * (i - last[p[x]]);
            last[p[x]] = i;
            pre[x] = sum[p[x]];
        }
    }

    for(int x = 1; x <= n; x++) {
        sum[p[x]] += rk[p[x]] * (w - last[p[x]]);
        last[p[x]] = w;
        ans[x] += sum[p[x]] - pre[x];
    }

    for(int i = 1; i <= n; i++) //要+1
        cout << fixed << setprecision(8) << (db)ans[i] / w + 1 << endl;
}

posted @ 2022-03-29 22:48  Bellala  阅读(46)  评论(0)    收藏  举报