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;
}

浙公网安备 33010602011771号