【模拟赛】货病(启发式合并,分块)
作者:互联网
背景
最长无笑话时间:45秒
——《Deadcodes》OI洞
题面
学校中有 n n n 个班级排成一行,每个班级都有一种独特的疫病(疫病源:OneInDark)。
接下来的 m m m 天,每一天都会发现班级 u i u_i ui 和班级 v i v_i vi 的频繁交流关系。而一整个交流圈的班级的疫病会全部共享。
同时,每天结束时 OneInDark 都会派老师巡查一段区间,若老师经过班级 i i i ,就会染上该班级共享的所有疫病。若老师染上所有疫病,OneInDark 就会很高兴。你希望最高效地让 OneInDark 高兴,所以想求出满足条件时巡查区间的最短长度。
每天都需要输出答案,每天都不独立。
输入
第一行三个整数 n , m , T n,m,T n,m,T , T T T 是强制在线系数。
接下来
m
m
m 行每行两个数
u
′
,
v
′
u',v'
u′,v′ ,其中
u
i
,
v
i
u_i,v_i
ui,vi 定义如下:
u
i
=
(
u
′
+
T
⋅
l
a
s
t
−
1
)
m
o
d
n
+
1
v
i
=
(
v
′
+
T
⋅
l
a
s
t
−
1
)
m
o
d
n
+
1
u_i=(u'+T\cdot last-1)\!\!\!\!\mod n +1\\ v_i=(v'+T\cdot last-1)\!\!\!\!\mod n +1
ui=(u′+T⋅last−1)modn+1vi=(v′+T⋅last−1)modn+1
其中 l a s t last last 表示上一次询问的答案,特别的,第一次询问的 l a s t = 0 last=0 last=0 。
输出
共 m m m 行,每行一个答案。
数据范围
1 ≤ n ≤ 1 0 5 , 1 ≤ m ≤ 2 × 1 0 5 , 1 ≤ u ′ , v ′ ≤ n 1\leq n\leq10^5,1\leq m\leq2\times10^5,1\leq u',v'\leq n 1≤n≤105,1≤m≤2×105,1≤u′,v′≤n 。
题解
首先有个很容易想到的结论:答案单调不增。
那么我们就有个策略,每次判断答案减 1 是否可行。
假设枚举答案为 X X X,我们考虑一个极大连通块,把里面每个成员班级从小到大排个序,那么 X X X 区间能够覆盖这个连通块,当且仅当区间不在相邻两个班级之间,或者开头之前结尾之后。那么对于两个相邻的班级 a , b a,b a,b , [ a + 1 , b − X ] [a+1,b-X] [a+1,b−X] 中的班级就不能作为 X X X 区间左端点。我们把所有不能作为左端点的位置标记了,如果整个序列有位置没被标记,那么 X X X 就是可行的。
但是当 X X X 变化的时候,每个标记区间 [ a + 1 , b − X ] [a+1,b-X] [a+1,b−X] 的右端点也会变化。所以我们在每个 a + 1 a+1 a+1 处记录一个 t a g a + 1 = b − n tag_{a+1}=b-n taga+1=b−n ,那么就可以从前往后扫描整个序列,得到是否有空位。
由于扫描整个序列是 O ( n ) O(n) O(n) 的需要优化,我们就将序列分块。当我们对连通块进行启发式合并的时候,每次修改一个点的 t a g tag tag ,就更新所在块 B B B 内每个点的信息: d i s i = i − max { t a g j ∣ j < i , j ∈ B } , s u f i = max { d i s j ∣ j ≥ i , j ∈ B } dis_i=i-\max\{tag_j|j<i,j\in B\},suf_i=\max\{dis_j|j\geq i,j\in B\} disi=i−max{tagj∣j<i,j∈B},sufi=max{disj∣j≥i,j∈B} 。我们最后扫描的时候,就依次访问每个块,利用先前访问的块的 t a g + ( n − X ) tag+(n-X) tag+(n−X) 最大值 r r r,查询该块的后缀 s u f r + 1 suf_{r+1} sufr+1 是否大于 n − X n-X n−X ,是就意味着有空位。
合理安排块大小,可以达到 O ( n log 2 n + n n log n ) O(n\log^2n+n\sqrt{n\log n}) O(nlog2n+nnlogn ) 。
CODE
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<random>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 100005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB long double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
#define SQ 150
int xchar() {
static const int maxn = 1000000;
static char b[maxn];
static int pos = 0,len = 0;
if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
if(pos == len) return -1;
return b[pos ++];
}
//#define getchar() xchar()
LL read() {
LL f = 1,x = 0;int s = getchar();
while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
if(!x) {putchar('0');return ;}
if(x<0) putchar('-'),x = -x;
return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}
const int MOD = 998244353;
int n,m,s,o,k;
int Max(int a,int b) {return a>b ? a:b;}
int rd[MAXN],bl[MAXN],ll[MAXN],rr[MAXN],cn;
int sf[MAXN],sfo[MAXN];
void upd(int x) {
int B = x/SQ+1,mx = -0x3f3f3f3f;
for(int i = ll[B];i <= rr[B];i ++) {
mx = Max(mx,rd[i]);
sf[i] = sfo[i] = i - mx;
}
for(int i = rr[B]-1;i >= ll[B];i --) sf[i] = Max(sf[i],sf[i+1]);
bl[B] = mx; return ;
}
set<int> a[MAXN],lm;
int fa[MAXN];
int findf(int x) {return fa[x]==x ? x:(fa[x]=findf(fa[x]));}
void unionSet(int A,int B) {
int u = findf(A),v = findf(B);
if(u == v) return ;
if(a[u].size() > a[v].size()) swap(A,B),swap(u,v);
lm.erase(*a[u].begin()); lm.erase(*a[v].begin());
a[u].erase(n<<1|1);
set<int> xg;
for(auto i = a[u].begin();i != a[u].end();i ++) {
int x = *i;
a[v].insert(x);
auto t = a[v].find(x),t2 = t;t2 ++;
if(*t2 > x+1) rd[x+1] = (*t2)-n;
else rd[x+1] = -0x3f3f3f3f;
xg.insert(x+1);
if(t != a[v].begin()) {
t --;
if(x > (*t)+1) rd[(*t)+1] = x-n;
else rd[(*t)+1] = -0x3f3f3f3f;
xg.insert((*t)+1);
}
}
lm.insert(*a[v].begin());
a[u].clear();
int pr = -0x3f3f3f3f;
for(auto i = xg.begin();i != xg.end();i ++) {
if(*i > pr) upd(*i),pr = rr[(*i)/SQ+1];
}
fa[u] = v;
return ;
}
bool check(int x) {
int mn = *(--lm.end()) - x;
int mr = n-x+1;
for(int i = 1;i <= cn && ll[i] <= mr;i ++) {
mn = Max(mn,ll[i]-1);
int ssf = -0x3f3f3f3f;
if(mr < rr[i]) {
for(int j = mr;j > mn;j --) ssf = Max(ssf,sfo[j]);
}else if(mn < rr[i]) ssf = sf[mn+1];
if(mn < rr[i] && ssf > n-x) return 1;
mn = Max(mn,bl[i]+(n-x));
}
return 0;
}
int main() {
freopen("currency.in","r",stdin);
freopen("currency.out","w",stdout);
n = read(); m = read(); int op = read();
cn = (n+1)/SQ+1;
for(int i = 1;i <= n;i ++) {
a[i].insert(i); fa[i] = i;
a[i].insert(n<<1|1); lm.insert(i);
rr[i/SQ+1] = i; rd[i] = -0x3f3f3f3f;
}rr[(n+1)/SQ+1] = n+1;
for(int i = n+1;i > 0;i --) ll[i/SQ+1] = i;
for(int i = 1;i <= n;i ++) rd[i+1] = n+1;
for(int i = 1;i <= cn;i ++) upd(ll[i]);
int las = n;
for(int i = 1;i <= m;i ++) {
s = (read() + op*las - 1) % n + 1;
o = (read() + op*las - 1) % n + 1;
unionSet(s,o);
while(las > 1 && check(las - 1)) las --;
AIput(las,'\n');
}
return 0;
}
标签:return,分块,int,mn,货病,MAXN,启发式,include,define 来源: https://blog.csdn.net/weixin_43960414/article/details/123020232