题目大意
给定 n n n 个人之间的好友关系,每次单点增加一个人的步数,求每个人在自己好友列表保持冠军的时间
解题思路
分块
我们将
n
n
n 个点分为大小点,定义
s
=
m
s = \sqrt m
s=m,对于度数大于
s
s
s 的点我们称为大点,对于度数小于等于
s
s
s 的点称为小点。可知,大点个数不超过
s
s
s。
对于小点的更新,我们直接暴力更新,复杂度不超过 m \sqrt m m
对于大点的更新,大点所连大点我们可以直接更新,复杂度也不超过 m \sqrt m m
对于大点所连大点,我们考虑开一个数组 c [ i ] [ j ] c[i][j] c[i][j] 表示第 i i i 个大点所连小点步数为 j j j 时的冠军小点。当一个大点 u u u 要增加步数 x x x 时,他所影响的小点只有步数范围为 ( w [ u ] , w [ u ] + x ] (w[u], w[u]+x] (w[u],w[u]+x] 的冠军小点。这样我们就更新完了大点所影响的所有点。但是怎么更新自己呢?对于所连大点的影响可以在更新的过程中记录一下最大值,但是我们不知道所连小点的最大值。所以我们要记录一个大点所连小点的最大值。这个在更新小点的时候更新。
Code
#include <bits/stdc++.h>
#define ll long long
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define fi first
#define se second
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define pb push_back
using namespace std;
const int MAXN = 2e5 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int n, m, q;
vector<int> g[MAXN];
vector<int> big[MAXN]; // 大点周围的大点
int win[MAXN], w[MAXN];
vector<int> c[507][10007];
int ans[MAXN];
int sz[MAXN];
int ls[MAXN];
int cnt;
int ma[MAXN]; // 大点周围小点的最大值
int ask(int x){
return ls[x];
}
void solve(){
cin >> n >> m >> q;
int s = sqrt(m*1.0);
while(m--){
int u, v;
cin >> u >> v;
g[u].pb(v);
g[v].pb(u);
}
for (int i = 1; i <= n; ++i){
sz[i] = g[i].size();
if(sz[i] > s){
ls[i] = ++cnt;
}
}
for (int i = 1; i <= n; ++i){
if(sz[i] > s){
for(auto j : g[i]){
if(sz[j] > s){
big[i].pb(j);
}
}
}
}
for (int i = 1; i <= q; ++i){
int v, x;
cin >> v >> x;
// 小点
if(sz[v] <= s){
int maxx = 0;
int now = w[v] + x;
for(auto j : g[v]){
if(sz[j] > s) ma[j] = max(ma[j], now);
maxx = max(maxx, w[j]);
if(win[j] && w[j] <= now){
ans[j] += i - win[j];
win[j] = 0;
}
}
w[v] = now;
// 更新大点冠军
if(maxx < now){
if(!win[v])
win[v] = i;
for(auto j : g[v]){
if(sz[j] > s){
int pos = ask(j);
c[pos][now].pb(v);
}
}
}
}
else{
int maxx = 0;
int now = w[v] + x;
// 大点周围的大点
for(auto j : big[v]){
maxx = max(maxx, w[j]);
if(win[j] && w[j] <= now){
ans[j] += i - win[j];
win[j] = 0;
}
}
maxx = max(maxx, ma[v]);
if(maxx < now && !win[v]){
win[v] = i;
}
int pos = ask(v);
for(int j = w[v] + 1; j <= now; ++j){
for(auto k : c[pos][j]){
if(win[k] && w[k] <= now){
ans[k] += i - win[k];
win[k] = 0;
}
}
}
w[v] = now;
}
}
for(int i = 1; i <= n; i++){
if(win[i]) {
ans[i] += q - win[i];
win[i] = 0;
}
}
for(int i = 1; i <= n; i++)
cout << ans[i] << endl;
}
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
qc;
int T;
// cin >> T;
T = 1;
while(T--){
solve();
}
return 0;
}