A - Average Rank Gym - 102500A
题意
有一些队,每周都有一些队过题,每过一题分数+1,问这些周之后每队的平均rank
题解
首先观察题目可以发现,规律就是当这个人分数增加时,所有和他分数相同的人rank+1,同时他的rank-(他当前分数+1的这个分数有多少人)
那么我们直接模拟??会T掉
对于每个人,只有在他的分数变化时再去更新 虽然优化了,但也会T掉
所以就有了一种特别妙的做法,因为每次要更新的人的分数是一样的,那我们不去记录每次跟他同样分数的rank变化,可以记下来当前point的贡献,记下来上次更新时的已经加完的贡献,在下次更新时,只去加贡献的差值
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define lowbit(a) ((a) & -(a))
#define clean(a, b) memset(a, b, sizeof(a))
const int mod = 998244353;
const int inf = 0x3f3f3f3f;
const int maxn = 3e5 + 9;
typedef pair<int,int>P;
int _;
//========================================================================
ll po[maxn],rk[maxn],last[maxn],sum[maxn],num[maxn],pre[maxn];
//========================================================================
int main()
{
int n,m,x,k;
scanf("%d%d",&n,&m);
// for(int i=1;i<=n;i++) rk[i]=1;
for(int i=1;i<=m;i++)
{
scanf("%d",&k);
for(int j=1;j<=k;j++)
{
scanf("%d",&x);
//当前分被更新
num[po[x]]+=rk[po[x]]*(i-1-last[po[x]]); // 这个分数当前贡献 += 这个分数保持了多少轮 * 这个分数当前名次
last[po[x]]=i-1; //记录上次更新的位置
rk[po[x]]++; //这个分的排名下降了 跟他名次相同的人的名次下降了
sum[x]+=num[po[x]]-pre[x]; // 这个人添加的贡献为 当前分的总贡献-上次分的贡献
//+1的分数也被更新
po[x]++; // 这个人分数+1
num[po[x]]+=rk[po[x]]*(i-1-last[po[x]]); //这个分当前贡献 += 这个分数保持了多少轮 * 这个分数当前名次
last[po[x]]=i-1; //记录上次更新的位置
pre[x]=num[po[x]]; //记录下这个人当前的贡献变为涨完分之后的贡献
}
}
for(int i=1;i<=n;i++) //把还没更新人的贡献加上
{
num[po[i]]+=rk[po[i]]*(m-last[po[i]]);
last[po[i]]=m;
sum[i]+=num[po[i]]-pre[i];
}
for(int i=1;i<=n;i++) printf("%.9f
",1.0+1.0*sum[i]/m); //计算的是贡献的增量 ,没有算初始增量所以要加上
return 0;
}