线段树分治是以时间点为下标,将修改以区间形式存储在线段树上,通过可撤销的数据结构进行单点查询(对于某些空间较小,难以实现撤销操作的数据结构如线性基可直接保存副本)。
用按秩合并的带权并查集维护连通块中点之间边数的奇偶性来判断是否存在负环(用栈储存撤销操作,复杂度 (O(nlog^2{n}))。
#include<bits/stdc++.h>
#define IL inline
#define ls k<<1
#define rs k<<1|1
#define pb push_back
#define LL long long
using namespace std;
const int N=1e5+3;
struct hh{
int x,y;
};
struct kk{
int x,y,op;
};
int n,m,k,dep[N],fa[N],dis[N];
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL int find_f(int x){if(x==fa[x]) return x;return find_f(fa[x]);}
IL int find_d(int x){if(x==fa[x]) return dis[x];return find_d(fa[x])^dis[x];}
struct segment{
vector<hh>a[N<<2];
void mdy(int k,int l,int r,int ll,int rr,int x,int y){
if(l>=ll&&r<=rr){a[k].pb((hh){x,y});return;}
int mid=l+r>>1;
if(ll<=mid) mdy(ls,l,mid,ll,rr,x,y);
if(rr>mid) mdy(rs,mid+1,r,ll,rr,x,y);
}
void solve(int k,int l,int r){
vector<kk>s;int mid=l+r>>1;
for(int i=0;i<a[k].size();++i){
int x=a[k][i].x,y=a[k][i].y;
int fx=find_f(x),fy=find_f(y),dx=find_d(x),dy=find_d(y);
if(fx==fy){
if(dx^dy^1){
for(int j=l;j<=r;++j) puts("No");
goto dd;
}
}
else{
int w=dx^dy^1;
if(dep[fx]>dep[fy]) swap(fx,fy),swap(dx,dy);
s.pb((kk){fx,fy,dep[fx]==dep[fy]});
fa[fx]=fy,dis[fx]=w,dep[fy]+=(dep[fx]==dep[fy]);
}
}
if(l==r) puts("Yes");
else solve(ls,l,mid),solve(rs,mid+1,r);
dd:for(int i=s.size()-1;~i;--i){
int x=s[i].x,y=s[i].y;
fa[x]=x,dis[x]=0,dep[y]-=s[i].op;
}
}
}T;
int main()
{
int x,y,l,r;
n=in(),m=in(),k=in();
for(int i=1;i<=n;++i) fa[i]=i;
while(m--)
x=in(),y=in(),l=in()+1,r=in(),
T.mdy(1,1,n,l,r,x,y);
T.solve(1,1,n);
return 0;
}
CF1140F Extending Set of Points
将 ((x,y)) 转化为连结 (x,y+n) 的边,得到一个二分图,易得该图中一个连通块的贡献为左边点的数量乘右边点的数量(可以将 ((x,y)) 看成坐标帮助理解)。
并查集维护。
#include<bits/stdc++.h>
#define IL inline
#define ls k<<1
#define rs k<<1|1
#define LL long long
#define pb push_back
#define M make_pair
using namespace std;
const int N=6e5+3;
int n;
map<pair<int,int>,int>mp;
map<pair<int,int>,int>::iterator it;
struct hh{
int fa[N],s1[N],s2[N];
int find(int x){return x^fa[x]?find(fa[x]):x;}
void set(int n){
for(int i=1;i<=2*n;++i) fa[i]=i;
for(int i=1;i<=n;++i) s1[i]=1,s2[i+n]=1;
}
}f;
struct kk{
int x,y;
};
struct segment{
vector<kk>a[N<<2];
void mdy(int k,int l,int r,int ll,int rr,kk u){
if(l>=ll&&r<=rr){a[k].pb(u);return;}
int mid=l+r>>1;
if(ll<=mid) mdy(ls,l,mid,ll,rr,u);
if(rr>mid) mdy(rs,mid+1,r,ll,rr,u);
}
void solve(int k,int l,int r,LL ans){
vector<kk>s;
for(int i=0;i<a[k].size();++i){
int x=f.find(a[k][i].x),y=f.find(a[k][i].y);
if(x^y){
ans-=1ll*f.s1[x]*f.s2[x]+1ll*f.s1[y]*f.s2[y];
if(f.s1[x]+f.s2[x]>f.s1[y]+f.s2[y]) swap(x,y);
f.fa[x]=y,f.s1[y]+=f.s1[x],f.s2[y]+=f.s2[x];
ans+=1ll*f.s1[y]*f.s2[y],s.pb((kk){x,y});
}
}
int mid=l+r>>1;
if(l==r) printf("%lld ",ans);
else solve(ls,l,mid,ans),solve(rs,mid+1,r,ans);
for(int i=s.size()-1;~i;--i){
int x=s[i].x,y=s[i].y;
ans-=1ll*f.s1[y]*f.s2[y];
f.fa[x]=x,f.s1[y]-=f.s1[x],f.s2[y]-=f.s2[x];
ans+=1ll*f.s1[x]*f.s2[x]+1ll*f.s1[y]*f.s2[y];
}
}
}T;
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
int main()
{
int x,y;
n=in();
for(int i=1;i<=n;++i){
x=in(),y=in()+3e5;
if(!mp[M(x,y)]) mp[M(x,y)]=i;
else T.mdy(1,1,n,mp[M(x,y)],i-1,(kk){x,y}),mp[M(x,y)]=0;
}
for(it=mp.begin();it!=mp.end();++it)
if(it->second) T.mdy(1,1,n,it->second,n,(kk){it->first.first,it->first.second});
f.set(3e5);
T.solve(1,1,n,0);
return 0;
}
垃圾题面,毁我青春。
很明显要用可持久化trie维护。
将修改作为区间储存发现难以维护可持久化trie。
考虑将询问作为区间储存,单点修改,每次到一个节点,将这个节点所包含的修改操作放到一起建trie,复杂度 (O(17n)),可以接受。
总复杂度 (O(17nlogn))。(交了一发结果rk1?
#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define pb push_back
#define ls k<<1
#define rs k<<1|1
using namespace std;
const int N=1e5+3;
int n,m,val[N],rt[N],ch[N*100][2],las[N*100],ans[N],cnt,c1,c2;
struct hh{
int l,r,x,day,d,id;
bool operator<(const hh &a) const{
return r<a.r;}
}q[N];
struct kk{
int s,v;
bool operator<(const kk &a) const{
return s<a.s;}
}b[N],c[N];
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL int new_node(){++cnt,ch[cnt][0]=ch[cnt][1]=las[cnt]=0;return cnt;}
void ins(int &o,int p,int now,int x,int dep){
o=new_node(),las[o]=now;
if(dep==-1) return;
int op=x>>dep&1;
ch[o][op^1]=ch[p][op^1];
ins(ch[o][op],ch[p][op],now,x,dep-1);
}
int ask(int o,int now,int x,int dep){
if(dep==-1) return 0;
int op=x>>dep&1;
if(las[ch[o][op^1]]>=now)
return ask(ch[o][op^1],now,x,dep-1)+(1<<dep);
return ask(ch[o][op],now,x,dep-1);
}
struct segment{
vector<hh>a[N<<2];
void mdy(int k,int l,int r,int ll,int rr,hh &u){
if(rr<l||ll>r) return;
if(l>=ll&&r<=rr){a[k].pb(u);return;}
int mid=l+r>>1;
if(ll<=mid) mdy(ls,l,mid,ll,rr,u);
if(rr>mid) mdy(rs,mid+1,r,ll,rr,u);
}
void solve(int k,int l,int r){
int num=0;cnt=0;
for(int i=l;i<=r;++i) b[++num]=c[i];
sort(b+1,b+num+1);
for(int i=1;i<=num;++i) rt[i]=0,ins(rt[i],rt[i-1],b[i].s,b[i].v,16);
for(int i=0;i<a[k].size();++i){
int pos=upper_bound(b+1,b+num+1,(kk){a[k][i].r,0})-b-1;
if(b[pos].s<a[k][i].l) continue;
ans[a[k][i].id]=max(ans[a[k][i].id],ask(rt[pos],a[k][i].l,a[k][i].x,16));
}
int mid=l+r>>1;
if(l==r) return;
solve(ls,l,mid),solve(rs,mid+1,r);
}
}T;
int main()
{
int op,l,r,x,d;
n=in(),m=in();
for(int i=1;i<=n;++i) val[i]=in();
for(int i=1;i<=m;++i){
op=in(),l=in(),r=in();
if(op) x=in(),d=in(),q[++c2]=(hh){l,r,x,c1,d,c2};
else c[++c1]=(kk){l,r};
}
for(int i=1;i<=c2;++i) T.mdy(1,1,c1,max(1,q[i].day-q[i].d+1),q[i].day,q[i]);
T.solve(1,1,c1),cnt=0;
for(int i=1;i<=n;++i) rt[i]=0,ins(rt[i],rt[i-1],i,val[i],16);
for(int i=1;i<=c2;++i) ans[i]=max(ans[i],ask(rt[q[i].r],q[i].l,q[i].x,16));
for(int i=1;i<=c2;++i) printf("%d
",ans[i]);
return 0;
}