链接:https://ac.nowcoder.com/acm/contest/11253/L
题意:n个人在竞争微信步数,每个人有自己的朋友圈(可抽象成无向图),当一个人在某一时间段的微信步数在自己的朋友圈最多并且大于0时,他就是当前时间段的冠军。一共有q个时间段,每个时间段只有1个人的步数在增加,问每个人一共有多少时间段在自己朋友圈是冠军。
题解:一个人是不是冠军只与他直接相连的点有关,因此可以想到分块,对度数大于$\sqrt{m}$的点和度数小于$\sqrt{m}$的点分别考虑。度数大于$\sqrt{m}$的点称为大点,最多有$\sqrt{m}$个;度数小于$\sqrt{m}$的点称为小点,这些点的度数最多是$\sqrt{m}$。
对于小点可以直接更新周围的点,对于大点周围的大点也可以直接更新,因为这些周围的点不超过$\sqrt{m}$。
对于大点周围的小点,注意到每个人的总步数不会超过10000步,因此可以对每个大点开一个vector,即C[ i ][ k ] 存放第i个大点周围步数为k的小点。在更新小点的时候,把它周围大点的C[ i ][ k ]顺便更新。这样当大点的步数更新时,枚举其vector中 step[u]+1到step[u]+x的小点,把它们的状态更新。对于大点此刻是否是冠军,可以记录一下每个大点周围小点步数的max,然后和它当前的步数比较。复杂度为 O($\sqrt{m}$W+q$\sqrt{m}$)。
记录答案可以当每个人得冠军时记录一个时刻,失去时把当前时刻与得冠军时的时间做差累加到当前这个人的答案中。
代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=2e5+10; const int M=460; const int W=1e4+10; vector<int>e[N]; vector<int>C[M][W]; vector<int>B[N],S[N]; int step[N]; //每个人当前的步数 int lst[N]; //每个人上次得冠军的时刻 int mp[N]; bool big[N],win[N]; int ans[N]; int mx[N]; //每个大点周围小点步数的最大值 int n,m,q; void Win(int u,int t){ win[u]=1; lst[u]=t; } void Lose(int u,int t){ ans[u]+=t-lst[u]; win[u]=0; } int main() { scanf("%d%d%d",&n,&m,&q); for(int i=1;i<=m;i++){ int u,v; scanf("%d%d",&u,&v); e[u].push_back(v); e[v].push_back(u); } int tot=0; for(int i=1;i<=n;i++){ if(e[i].size()>M){ mp[i]=++tot; big[i]=1; } } for(int i=1;i<=n;i++){ if(big[i]){ for(int v: e[i]){ if(big[v]){ B[i].push_back(v); } else S[i].push_back(v); } } } for(int i=1;i<=m;i++){ int u,x; scanf("%d%d",&u,&x); if(!big[u]){ bool f=1; //判断当前这个人是否是冠军 step[u]+=x; for(int v: e[u]){ if(step[v]>=step[u]){ f=0; } if(win[v]&&step[v]<=step[u]){ Lose(v,i); } if(big[v]){ C[mp[v]][step[u]].push_back(u); mx[v]=max(mx[v],step[u]); } } if(!win[u]&&f) Win(u,i); } else{ bool f1=1; for(int v: B[u]){ if(step[v]>=step[u]+x){ f1=0; } if(win[v]&&step[v]<=step[u]+x){ Lose(v,i); } } bool f2=(mx[u]<step[u]+x); for(int j=step[u]+1;j<=step[u]+x;j++){ for(int v: C[mp[u]][j]){ if(win[v]&&step[v]<=step[u]+x){ Lose(v,i); } } } step[u]+=x; if(f1&&f2&&!win[u]) Win(u,i); } } for(int i=1;i<=n;i++){ if(win[i]){ Lose(i,m); } } for(int i=1;i<=n;i++){ printf("%d\n",ans[i]); } return 0; }