题解:CF799F Beautiful fountains rows
前言
本文同步自洛谷专栏,题目传送门。
同时有 xor-hashing 和线段树做法。
分析
可以先减去全 \(0\) 区间带来的贡献,统一形式,具体数值推一推式子即可。
序列问题可以首先考虑扫描线(个人实现为扫描右端点),下面考虑如何维护贡献。
异或哈希做法
下面将各类限制称为颜色。
观察到限制形如:所有颜色的出现次数都是 \(0\) 次或奇数次。
如果要求全为偶数次,xor-hashing 是比较容易的,即要求区间异或和为 \(0\)。
$\red{\text{xor-hashing}}$
具体做法是:给每一个颜色赋一个随机权值,每一个点的权值即为该点上所有颜色的权值的异或和,区间异或和(一般用到前缀和)为 \(0\) 代表所有颜色恰出现偶数次。
错误率很低,担心运气不好的话,可以多随几组权值写成结构体,各组答案均为 \(0\) 才代表正确。
事实上,只需要将各个颜色区间和答案区间的左端点 \(+1\) 即可完成转化。(可能有点抽象,可结合正确性证明理解)
正确性证明如下:
有颜色 \(c\) 覆盖区间 \([L,R]\),当前扫描到了 \(r\),一个可行答案为 \(l\)。
则需证明:区间 \([L+1,R]\) 均打上颜色 \(c\),区间 \([l+1,r]\) 中颜色 \(c\) 出现了偶数次 \(\Leftrightarrow\) 区间 \([L,R]\) 均打上颜色 \(c\),区间 \([l,r]\) 中颜色 \(c\) 出现了奇数次或 \(0\) 次。
情况 \(l\ge R,r\le L,l\le L\le R\le r,L\le l\le r\le R\) 为相离或包含,容易验证,
若 \(L\le l\le R\le r\),则 \(l+1\) 恰能改变奇偶性,若 \(l\le L\le r\le R\),则 \(L+1\) 恰能改变奇偶性。
上述证明情况可能有重复,但不会遗漏。
统计上,开两个 map 分别维护某哈希值的出现次数与出现时的下标和,容易处理。
时间复杂度 \(O(n+m\log{m})\),采用哈希表维护可以做到 \(O(n+m)\),但不清楚会不会被 hack。
线段树做法
考虑颜色区间 \([L,R]\) 在当前扫描到的 \(r\) 产生的限制情况。
对于 \(R\le r\),则所有 \(L\le i\le R\) 与 \(R\) 奇偶不同的 \(i\) 均不可行,这一限制在扫描线过程中每次处理 \(R=r\) 的即可。
对于 \(R>r\),则找出 \(<r\) 最大的且奇偶不同的 \(L_1\),\([1,L_1]\) 均不可行,并另外找出最小的 \(L_2\),\([L_2,r]\) 中与 \(r\) 奇偶不同的均不可行。对于每个 \(r\) 处理,查询后需要撤销。
由于分奇偶考虑,则可以分奇偶开线段树支持查询、开 set 处理第二种情况。
线段树每个结点维护最小值、最小值出现次数、最小值权值和和懒标记即可,区间不可行即为区间 \(+1\)。
时间复杂度 \(O((n+m)\log{m})\)。
实现
$\red{\text{xor-hashing}}$
#pragma optimize("O2")
#include<bits/stdc++.h>
using namespace std;
#define N 200005
#define gc() (rp1==rp2&&(rp2=(rp1=buf)+fread(buf,1,1<<22,stdin)),*rp1++)
#define ull unsigned long long
mt19937_64 rnd(std::chrono::high_resolution_clock::now().time_since_epoch().count());
char buf[1<<22],*rp1,*rp2;
int n,m,d[N],lst;
ull sum[N],A[N],ans=0,now;
map<ull,ull>cnt,tot;
inline int rd(){
int a=0,c=gc();
while(!isdigit(c)) c=gc();
while(isdigit(c)) a=10*a+(c^48),c=gc();
return a;
}
int main(){
n=rd(),m=rd();//抹去左端点,则 0/奇数次->偶数次
for(int i=1,l,r;i<=n;i++){//差分
l=rd(),r=rd(),d[l]++,d[r+1]--,now=rnd();
sum[l]^=now,A[l]^=now,A[r+1]^=now;
}
for(int i=1;i<=m;i++) A[i]^=A[i-1],sum[i]^=A[i];
for(int i=1;i<=m;i++){
d[i]+=d[i-1],sum[i]^=sum[i-1];
tot[sum[i]]+=i-1,cnt[sum[i]]++;//注意先后顺序
ans+=cnt[sum[i]]*i-tot[sum[i]];
}
for(int i=1;i<=m;i++){
if(d[i]==0) lst++;
else ans-=1ll*lst*(lst+1)*(lst+2)/6,lst=0;
}
printf("%llu",ans-1ll*lst*(lst+1)*(lst+2)/6);
return 0;
}
线段树部分采用 zkw 线段树写法,应用了标记永久化,用时为前者约 \(2.5\) 倍。
$\red{\text{code}}$
#pragma optimize("O2")
#include<bits/stdc++.h>
using namespace std;
#define N 200005
#define gc() (rp1==rp2&&(rp2=(rp1=buf)+fread(buf,1,1<<22,stdin)),*rp1++)
#define mid ((l+r)>>1)
#define mn(a) tr[a].minn
#define tg(a) tr[a].tag
#define ct(a) tr[a].cnt
#define sm(a) tr[a].sum
char buf[1<<22],*rp1,*rp2;
int n,m,lst,up,d[N],e[N];
vector<int>L[N];
set<int>st[2];
long long ans=0;
inline int rd(){
int a=0,c=gc();
while(!isdigit(c)) c=gc();
while(isdigit(c)) a=10*a+(c^48),c=gc();
return a;
}
class segtree{//改造 zkw 线段树
private:
struct node{
int minn,tag,cnt;long long sum;
inline void clear(){minn=n,tag=0,cnt=0,sum=0;}
}tr[N<<2];
inline void pushup(int p){
mn(p)=min(mn(p<<1),mn(p<<1|1)),ct(p)=0,sm(p)=0;
if(mn(p<<1)==mn(p)) ct(p)+=ct(p<<1),sm(p)+=sm(p<<1);
if(mn(p<<1|1)==mn(p)) ct(p)+=ct(p<<1|1),sm(p)+=sm(p<<1|1);
mn(p)+=tg(p);//标记永久化
}
public:
void build(int f){
tr[up+1].clear(),tr[up].clear();//统一写法
for(int i=f;i<=m;i+=2){
tr[i+up+1].clear(),mn(i+up)=0;
ct(i+up)=1,sm(i+up)=i-1;
}
for(int i=up-1;i>=1;i--) tg(i)=0,pushup(i);
}
void modify(int l,int r,int d){
for(l+=up-1,r+=up+1;l<(r^1);){//(l,r)
if(~l&1) mn(l+1)+=d,tg(l+1)+=d;
if(r&1) mn(r-1)+=d,tg(r-1)+=d;
l>>=1,r>>=1,pushup(l),pushup(r);
}
for(l>>=1;l;l>>=1) pushup(l);
}
void query(int qr,int s,int p=1,int l=0,int r=up-1){
if(r<=qr){//这里采用递归较为方便
if(mn(p)+s==0) ans+=1ll*qr*ct(p)-sm(p);
return;
}
s+=tg(p),query(qr,s,p<<1,l,mid);
if(mid<qr) query(qr,s,p<<1|1,mid+1,r);
}
}T[2];//每棵线段树仅维护奇数或偶数的答案
inline void check(int i,int x){//处理右端点 r>i 的情况
if(!st[0].empty()||!st[1].empty()){//向奇偶不同的左端点加上限制
int l=m;
if(!st[0].empty()) l=min(l,*st[0].begin());
if(!st[1].empty()) l=min(l,*st[1].begin());
if(l<=i) T[~i&1].modify(l,i,x);
}//向不可行前缀施加限制
auto it=st[~i&1].lower_bound(i);
if(it!=st[~i&1].begin()){
it--,T[0].modify(1,*it,x),T[1].modify(1,*it,x);
}
}
int main(){
n=rd(),m=rd(),up=1<<__lg(m<<1);
T[1].build(1),T[0].build(2);
for(int i=1,l,r;i<=n;i++){//差分
l=rd(),r=rd(),d[l]++,d[r+1]--;
L[r].emplace_back(l),e[l]++;
}
for(int i=1;i<=m;i++){
d[i]+=d[i-1];
if(d[i]==0) lst++;
else ans-=1ll*lst*(lst+1)*(lst+2)/6,lst=0;
}
for(int i=1;i<=m;i++){
if(e[i]) st[i&1].insert(i);
for(auto &l:L[i]){//处理 r<=i 的情况
T[~i&1].modify(l,i,1),e[l]--;//ban 掉
if(e[l]==0) st[l&1].erase(l);
if((l+i+1)&1) continue;
T[0].modify(1,l,1),T[1].modify(1,l,1);//ban 掉
}
check(i,1),T[1].query(i,0),T[0].query(i,0);
check(i,-1);//复原
}
printf("%lld",ans-1ll*lst*(lst+1)*(lst+2)/6);
return 0;
}

浙公网安备 33010602011771号