线段树
线段树
基础的就不写了
动态开点
其实是很简单
就是将节点重编号,做到用的到再开。
对于权值线段树或值域特大操作较少的线段树非常有用。
就是注意离散化和动态开点还是有区别的:
- 动态开点是 \(\log \text{值域}\) 的时空复杂度,离散化是 \(\log N\) 的复杂度,常数还是有差距的(一般是卡常问题)。
- 在线段树合并时,离散化的空间复杂度无法保证,相当于是 \(nm\) 的(\(m\) 是线段树个数)。
所以有的题就既要离散化,又要动态开点,如领导集团问题(如果是打假了请D我,顺便告诉我正确的>(如果是打假了请D我,顺便告诉我正确的
好像很少有专门例题,这里放一个 P3372【模板】线段树1 的动态开点写法(码风略毒
CODE
#include<bits/stdc++.h>
using namespace std;
typedef long long llt;
const int N=1e5+3;
int n,m;
namespace OI{
template<typename T>
inline void read(T &x){
char s=getchar();x=0;bool pd=false;
while(s<'0'||'9'<s){if(s=='-') pd=true;s=getchar();}
while('0'<=s&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
if(pd) x=-x;
}
template<typename T,typename... Args>
inline void read(T& x,Args&... args){read(x);read(args...);}
template<typename T>
inline void write(T x){
static T st[45];T top=0;
if(x<0)x=~x+1,putchar('-');
do{st[top++]=x%10;}while(x/=10);
while(top)putchar(st[--top]^48);
}
inline void write(const char c){putchar(c);}
inline void write(const char c[]){
int len=strlen(c);
for(int i=0;i<len;i++) putchar(c[i]);
}
template<typename T,typename... Args>
inline void write(T x,Args... args){write(x);write(args...);}
}
#define read OI::read
#define write OI::write
class Stree{
#define mid ((l+r)>>1)
#define lid lt[id]
#define rid rt[id]
private:
int lt[N<<1],rt[N<<1],tot_=1;
llt sum[N<<1],lz[N<<1];
public:
inline void pushup(int id){
sum[id]=sum[lid]+sum[rid];
}
inline void pushdown(int l,int r,int id){
if(lz[id]){
if(lid||!lid){
sum[lid]+=(mid-l+1)*lz[id];
lz[lid]+=lz[id];
}
if(rid||!rid){
sum[rid]+=(r-mid)*lz[id];
lz[rid]+=lz[id];
}
lz[id]=0;
}
}
void add(int L,int R,int l,int r,int t,int& id){
if(!id) id=++tot_;
if(L<=l&&r<=R) sum[id]+=t*(r-l+1),lz[id]+=t;
else{
pushdown(l,r,id);
if(L<=mid) add(L,R,l,mid,t,lid);
if(R>mid) add(L,R,mid+1,r,t,rid);
pushup(id);
}
}
llt get(int L,int R,int l,int r,int id){
if(!id) return 0;
if(L<=l&&r<=R) return sum[id];
pushdown(l,r,id);
llt res=0;
if(L<=mid) res+=get(L,R,l,mid,lid);
if(mid<R) res+=get(L,R,mid+1,r,rid);
return res;
}
#undef mid
#undef lid
#undef rid
}st;
signed main(){
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
freopen("out.out","w",stdout);
#endif
read(n,m);
for(int i=1;i<=n;i++){
int x,id=1;read(x);
st.add(i,i,1,n,x,id);
}
for(int i=1;i<=m;i++){
int p,x,y,k,id=1;read(p,x,y);
if(p==1) read(k),st.add(x,y,1,n,k,id);
else write(st.get(x,y,1,n,1),'\n');
}
}
权值线段树:
可以说就是将桶数组变成线段树,就可以维护一些其他东西,因为是值域的大小,一般结合动态开点、离散化。
挺好理解,看看例题就差不多了。
例题
-
用权值线段树维护区间和:
-
插入/删除 \(x\):
就在 \(x\) 的位置 \(\pm 1\)
-
查询 \(x\) 数的排名:
统计 \(1\) 到 \(x-1\) 的区间和 \(+1\)
-
查询排名为 \(x\) 的数:
寻找一个位置 \(a\) 使得 \(qsum_{a-1}\le x \le qsum_a\) ( \(qsum_x\) 表示 \(x\) 位置的前缀和
这个过程可以通过动态减在递归时实现
具体的可以看代码
-
查询 \(x\) 的前驱:
这个可以通过 \(2,3\) 实现
查询以 \(x\) 的排名 \(-1\) 为排名的数
比较好理解的,可以举几个例子试一下。
-
查询 \(x\) 的后继:
类似于是前驱,也通过 \(2,3\) 实现
查询以 \(x+1\) 的排名为排名的数
也比较好理解。
可以稍微思考一下为什么 \(4\) 是排名减一而 \(5\) 是数加一
这里给出一组数据:
点击查看
1 1 1 3 1 3 1 4 5 3 6 3有没理解的可以看代码:
CODE
#include<bits/stdc++.h> using namespace std; typedef long long llt; const int N=1e5+3,Max=1e7+3; struct Stree{int l,r,sum;}ct[N<<2]; int n,tot_=1; namespace OI{ template<typename T> inline void read(T &x){ char s=getchar();x=0;bool pd=false; while(s<'0'||'9'<s){if(s=='-') pd=true;s=getchar();} while('0'<=s&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar(); if(pd) x=-x; } template<typename T,typename... Args> inline void read(T& x,Args&... args){read(x);read(args...);} template<typename T> inline void write(T x){ static T st[45];T top=0; if(x<0)x=~x+1,putchar('-'); do{st[top++]=x%10;}while(x/=10); while(top)putchar(st[--top]^48); } template<typename T> inline void write(T x,const char c){write(x);putchar(c);} template<typename T,typename... Args> inline void write(T& x,Args&... args){write(x);putchar(' '),write(args...);} } #define read OI::read #define write OI::write #define read OI::read #define write OI::write namespace{ #define mid ((l+r)>>1) #define lid ct[id].l #define rid ct[id].r inline void pushup(int id){ct[id].sum=ct[lid].sum+ct[rid].sum;} void add(int l,int r,int x,int t,int& id){ if(!id) id=++tot_; if(l==r) ct[id].sum+=t; else{ if(x<=mid) add(l,mid,x,t,lid); if(mid<x) add(mid+1,r,x,t,rid); pushup(id); } } int getrank(int l,int r,int x,int id){ if(!id) return -1; if(l==r) return 1; if(x<=mid) return getrank(l,mid,x,lid); return ct[lid].sum+getrank(mid+1,r,x,rid); } int getval(int l,int r,int x,int id){ if(l==r) return l; if(ct[lid].sum<x) return getval(mid+1,r,x-ct[lid].sum,rid); return getval(l,mid,x,lid); } #undef mid #undef lid #undef rid } signed main(){ #ifndef ONLINE_JUDGE freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif read(n); for(int i=1;i<=n;i++){ int p,x,id=1; read(p,x); if(p==1) add(-Max,Max,x,1,id); else if(p==2) add(-Max,Max,x,-1,id); else if(p==3) write(getrank(-Max,Max,x,id),'\n'); else if(p==4) write(getval(-Max,Max,x,id),'\n'); else if(p==5) write(getval(-Max,Max,getrank(-Max,Max,x,id)-1,id),'\n'); else write(getval(-Max,Max,getrank(-Max,Max,x+1,id),id),'\n'); } }
-
线段树合并:
就是将几棵线段树两两合并到一起的算法,一般是将值合和标记合并(如区间加),有的也将标记下移到叶子(或未开节点)合并(如this)(但好像几乎不会遇见
总的来说是将线段树的信息合并,但方法因题而异。
例题可以直接看下面,这里放个板子。
CODE
void merge(int& id1,int& id2,int l,int r){
if(!id1||!id2) id1+=id2;
else{
sum[id1]+=sum[id2],tg[id1]+=tg[id2];//合并信息
merge(lt[id1],lt[id2],l,mid);
merge(rt[id1],rt[id2],mid+1,r);
}
}
例题:
1. 蒟蒻的数列
有点诈骗
因为是最后输出和,可以离线
将所有操作离线后按修改的值升序
然后就变成了区间覆盖,区间求和
可以离散化或动态开点做
CODE
#include<bits/stdc++.h>
using namespace std;
typedef long long llt;
#define int llt
const int N=4e4+3,M=1e9+1;
int n;
struct Ques{
int x,y,k;
bool operator<(Ques a){return this->k<a.k;}
}c[N];
namespace IO{
template<typename T> inline void read(T &x){
char s=getchar();x=0;bool pd=false;
while(s<'0'||'9'<s){if(s=='-') pd=true;s=getchar();}
while('0'<=s&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
if(pd) x=-x;
}
template<typename T,typename... Args> inline void read(T& x,Args&... args){read(x);read(args...);}
template<typename T> inline void write(T x){
static T st[45];T top=0;
if(x<0)x=~x+1,putchar('-');
do{st[top++]=x%10;}while(x/=10);
while(top)putchar(st[--top]^48);
}
inline void write(const char c){putchar(c);}
inline void write(const char *c){
int len=strlen(c);
for(int i=0;i<len;i++) putchar(c[i]);
}
template<typename T> inline void Write(T x){write(x);putchar(' ');}
inline void Write(const char c){write(c);}
inline void Write(const char *c){write(c);}
template<typename T,typename... Args> inline void write(T x,Args... args){write(x);write(args...);}
template<typename T,typename... Args> inline void Write(T x,Args... args){Write(x);Write(args...);}
}
#define read IO::read
#define write IO::write
#define Write IO::Write
class Stree{
private:
int lt[N*30],rt[N*30],sum[N*30],s[N*30],tot_=1;
public:
#define mid ((l+r)>>1)
inline void pushup(int id){sum[id]=sum[lt[id]]+sum[rt[id]];}
inline void pushdown(int l,int r,int id){
if(s[id]){
int ls=s[id];s[id]=0;
if(!lt[id])lt[id]=++tot_;
if(!rt[id])rt[id]=++tot_;
s[lt[id]]=ls;
s[rt[id]]=ls;
sum[lt[id]]=(mid-l+1)*ls;
sum[rt[id]]=(r-mid)*ls;
}
}
void updata(int L,int R,int l,int r,int t,int& id){
if(!id) id=++tot_;
if(L<=l&&r<=R) sum[id]=(r-l+1)*t,s[id]=t;
else{
pushdown(l,r,id);
if(L<=mid) updata(L,R,l,mid,t,lt[id]);
if(mid<R) updata(L,R,mid+1,r,t,rt[id]);
pushup(id);
}
}
int getsum(int L,int R,int l,int r,int id){
if(!id) return 0;
if(L<=l&&r<=R) return sum[id];
pushdown(l,r,id);
int res=0;
if(L<=mid) res+=getsum(L,R,l,mid,lt[id]);
if(mid<R) res+=getsum(L,R,mid+1,r,rt[id]);
return res;
}
#undef mid
}st;
signed main(){
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
freopen("out.out","w",stdout);
#endif
read(n);
for(int i=1;i<=n;i++) read(c[i].x,c[i].y,c[i].k);
sort(c+1,c+1+n);
for(int id=1,i=1;i<=n;i++) st.updata(c[i].x,c[i].y-1,1,M,c[i].k,id);
write(st.getsum(1,M,1,M,1));
}
2.Promotion Counting P
对每个节点维护一棵线段树,在第 \(i\) 节点回溯合并后查询 \(\sum_{k=i+1}^N p_k\)
CODE
#include<bits/stdc++.h>
using namespace std;
typedef long long llt;
const int N=1e5+3,MAX=1e9+1;
int n,cv[N],ans[N];
namespace IO{
template<typename T> inline void read(T &x){
char s=getchar();x=0;bool pd=false;
while(s<'0'||'9'<s){if(s=='-') pd=true;s=getchar();}
while('0'<=s&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
if(pd) x=-x;
}
template<typename T,typename... Args> inline void read(T& x,Args&... args){read(x);read(args...);}
template<typename T> inline void write(T x){
static T st[45];T top=0;
if(x<0)x=~x+1,putchar('-');
do{st[top++]=x%10;}while(x/=10);
while(top)putchar(st[--top]^48);
}
inline void write(const char c){putchar(c);}
inline void write(const char *c){
int len=strlen(c);
for(int i=0;i<len;i++) putchar(c[i]);
}
template<typename T> inline void Write(T x){write(x);putchar(' ');}
inline void Write(const char c){write(c);}
inline void Write(const char *c){write(c);}
template<typename T,typename... Args> inline void write(T x,Args... args){write(x);write(args...);}
template<typename T,typename... Args> inline void Write(T x,Args... args){Write(x);Write(args...);}
}
#define read IO::read
#define write IO::write
#define Write IO::Write
class Stree{
private:
int rt[N*50],lt[N*50],sum[N*50],tot_;
public:
#define mid ((l+r)>>1)
int ro[N*50];
inline void pushup(int id){sum[id]=sum[lt[id]]+sum[rt[id]];}
void add(int x,int l,int r,int t,int& id){
if(!id) id=++tot_;
if(l==r) sum[id]+=t;
else{
if(x<=mid) add(x,l,mid,t,lt[id]);
else add(x,mid+1,r,t,rt[id]);
pushup(id);
}
}
int getans(int x,int l,int r,int id){
if(!id) return 0;
if(l==r) return sum[id];
if(x<=mid) return getans(x,l,mid,lt[id])+sum[rt[id]];
else return getans(x,mid+1,r,rt[id]);
}
void merge(int& id1,int id2,int l,int r){
if(!id1||!id2) id1+=id2;
else if(l==r) sum[id1]+=sum[id2];
else{
merge(lt[id1],lt[id2],l,mid);
merge(rt[id1],rt[id2],mid+1,r);
pushup(id1);
}
}
}st;
class TO{
private:
int hd[N],nt[N<<1],to[N<<1],tot_;
public:
#define For_to(i,x) for(int i=hd[x];~i;i=nt[i])
TO(){memset(hd,-1,sizeof hd);}
inline void add(int x,int y){
to[tot_]=y,nt[tot_]=hd[x],hd[x]=tot_++;
}
void dfs(int x){
For_to(i,x){
int y=to[i];
dfs(y);
st.merge(st.ro[x],st.ro[y],1,MAX);
}
ans[x]=st.getans(cv[x]+1,1,MAX,st.ro[x]);
}
#undef For_to
}to;
signed main(){
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
freopen("out.out","w",stdout);
#endif
read(n);
for(int i=1;i<=n;i++) read(cv[i]),st.add(cv[i],1,MAX,1,st.ro[i]);
for(int i=2;i<=n;i++){
int x;read(x);
to.add(x,i);
}
to.dfs(1);
for(int i=1;i<=n;i++) write(ans[i],'\n');
}
还可以先dfs序,就变成了求静态区间rank。
具体就是先离散化后将所有序列里的值和询问按值降序,在树状数组(线段树)按序列中的数的原位置依次加一,再加完所有 \(\ge k\) 的数后对值 \(=k\) 的询问查询区间和。知乎上的解法(方法一)
3.雨天的尾巴
树上差分 \(+\) 线段树合并
首先,对于每个节点,开一棵动开线段树,用来存储标记的类型
然后的链修改,就按树上差分对节点加对应类型的标记。
回溯时将线段树合并,标记也就合并了。
CODE
#include<bits/stdc++.h>
using namespace std;
typedef long long llt;
const int N=1e5+3;
int n,m,ans[N];
namespace IO{
template<typename T> inline void read(T &x){
char s=getchar();x=0;bool pd=false;
while(s<'0'||'9'<s){if(s=='-') pd=true;s=getchar();}
while('0'<=s&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
if(pd) x=-x;
}
template<typename T,typename... Args> inline void read(T& x,Args&... args){read(x);read(args...);}
template<typename T> inline void write(T x){
static T st[45];T top=0;
if(x<0)x=~x+1,putchar('-');
do{st[top++]=x%10;}while(x/=10);
while(top)putchar(st[--top]^48);
}
inline void write(const char c){putchar(c);}
inline void write(const char *c){
int len=strlen(c);
for(int i=0;i<len;i++) putchar(c[i]);
}
template<typename T> inline void Write(T x){write(x);putchar(' ');}
inline void Write(const char c){write(c);}
inline void Write(const char *c){write(c);}
template<typename T,typename... Args> inline void write(T x,Args... args){write(x);write(args...);}
template<typename T,typename... Args> inline void Write(T x,Args... args){Write(x);Write(args...);}
}
#define read IO::read
#define write IO::write
#define Write IO::Write
class Stree{
private:
int tot_,lt[N*100],rt[N*100],ma[N*100],sum[N*100];
public:
int root[N];
#define mid (l+r>>1)
inline void pushup(int id){
sum[id]=sum[lt[id]]+sum[rt[id]];
ma[id]=max(ma[lt[id]],ma[rt[id]]);
}
void add(int x,int l,int r,int t,int& id){
if(!id) id=++tot_;
if(l==r) sum[id]+=t,ma[id]=sum[id];
else{
if(x<=mid) add(x,l,mid,t,lt[id]);
else add(x,mid+1,r,t,rt[id]);
pushup(id);
}
}
int getmax(int l,int r,int id){
if(!id||ma[id]<=0) return 0;
if(l==r) return l;
if(ma[lt[id]]>=ma[rt[id]]) return getmax(l,mid,lt[id]);
else return getmax(mid+1,r,rt[id]);
}
void merge(int& id1,int id2,int l,int r){
if(!id1||!id2) id1+=id2;
else if(l==r)
sum[id1]+=sum[id2],ma[id1]=sum[id1];
else{
merge(lt[id1],lt[id2],l,mid);
merge(rt[id1],rt[id2],mid+1,r);
pushup(id1);
}
}
#undef mid
}st;
class TO{
private:
int fa[N][21],dep[N],hd[N],nt[N<<1],to[N<<1],tot_;
public:
#define For_to(i,x) for(int i=hd[x];~i;i=nt[i])
TO(){memset(hd,-1,sizeof hd);}
inline void add(int x,int y){
to[tot_]=y,nt[tot_]=hd[x],hd[x]=tot_++;
}
void init(int x,int d){
dep[x]=d;
For_to(i,x){
int y=to[i];
if(!dep[y]){
fa[y][0]=x;
for(int i=1;i<=20;i++) fa[y][i]=fa[fa[y][i-1]][i-1];
init(y,d+1);
}
}
}
int getlca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=20;i>=0;i--) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
if(x==y) return x;
for(int i=20;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int getf(int x){return fa[x][0];}
void dfs(int x){
For_to(i,x){
int y=to[i];
if(y!=getf(x)){
dfs(y);
st.merge(st.root[x],st.root[y],1,N);
}
}
ans[x]=st.getmax(1,N,st.root[x]);
}
#undef For_to
}to;
int main(){
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
freopen("out.out","w",stdout);
#endif
read(n,m);
for(int i=1;i<n;i++){
int x,y;read(x,y);
to.add(x,y),to.add(y,x);
}
to.init(1,1);
for(int i=1;i<=m;i++){
int x,y,z;read(x,y,z);
int w=to.getlca(x,y);
st.add(z,1,N,1,st.root[x]);
st.add(z,1,N,1,st.root[y]);
st.add(z,1,N,-1,st.root[w]);
st.add(z,1,N,-1,st.root[to.getf(w)]);
}
to.dfs(1);
for(int i=1;i<=n;i++) write(ans[i],'\n');
}
4. 魔法少女LJJ
有点诈骗。
稍微观察一下数据范围,可以发现 \(c \le 7\),所以不会有 \(8,9\) 操作。
唯一难点就是维护 \(6\) 积的大小,由于数据范围,不能直接乘。
考虑 \(\log(n)\log(m)=\log(nm)\) 且 \(n<m \Leftrightarrow \log(n)<\log(m)\)
所以可以直接维护 \(\log\) 的区间积。
CODE
#include<bits/stdc++.h>
using namespace std;
typedef long long llt;
const int N=4e5+3,MAX=1e9+1;
int n,tot_;
namespace IO{
template<typename T> inline void read(T &x){
char s=getchar();x=0;bool pd=false;
while(s<'0'||'9'<s){if(s=='-') pd=true;s=getchar();}
while('0'<=s&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
if(pd) x=-x;
}
template<typename T,typename... Args> inline void read(T& x,Args&... args){read(x);read(args...);}
template<typename T> inline void write(T x){
static T st[45];T top=0;
if(x<0)x=~x+1,putchar('-');
do{st[top++]=x%10;}while(x/=10);
while(top)putchar(st[--top]^48);
}
inline void write(const char c){putchar(c);}
inline void write(const char *c){
int len=strlen(c);
for(int i=0;i<len;i++) putchar(c[i]);
}
template<typename T> inline void Write(T x){write(x);putchar(' ');}
inline void Write(const char c){write(c);}
inline void Write(const char *c){write(c);}
template<typename T,typename... Args> inline void write(T x,Args... args){write(x);write(args...);}
template<typename T,typename... Args> inline void Write(T x,Args... args){Write(x);Write(args...);}
}
#define read IO::read
#define write IO::write
#define Write IO::Write
class Stree{
private:
int rt[N*20],lt[N*20],sum[N*20],tot_=1;
bool cl[N*20];
public:
#define mid ((l+r)>>1)
int ro[N*20];
double lg[N*20];
inline void pushup(int id){
sum[id]=sum[lt[id]]+sum[rt[id]];
lg[id]=lg[lt[id]]+lg[rt[id]];
}
inline void pushdown(int l,int r,int id){
if(cl[id]){
cl[id]=0;
if(lt[id]){
cl[lt[id]]=1;
sum[lt[id]]=0;
lg[lt[id]]=0;
}
if(rt[id]){
cl[rt[id]]=1;
sum[rt[id]]=0;
lg[rt[id]]=0;
}
}
}
int getsum(int L,int R,int l,int r,int id){
if(!id) return 0;
if(L<=l&&r<=R) return sum[id];
pushdown(l,r,id);
int res=0;
if(L<=mid) res+=getsum(L,R,l,mid,lt[id]);
if(R>mid) res+=getsum(L,R,mid+1,r,rt[id]);
return res;
}
void add(int x,int l,int r,int t,int& id){
if(!id) id=++tot_;
if(l==r) sum[id]+=t,lg[id]+=log10(l)*t;
else{
pushdown(l,r,id);
if(x<=mid) add(x,l,mid,t,lt[id]);
else add(x,mid+1,r,t,rt[id]);
pushup(id);
}
}
void clr(int L,int R,int l,int r,int id){
if(!id) return ;
if(L<=l&&r<=R) sum[id]=0,lg[id]=0,cl[id]=1;
else{
pushdown(l,r,id);
if(L<=mid) clr(L,R,l,mid,lt[id]);
if(R>mid) clr(L,R,mid+1,r,rt[id]);
pushup(id);
}
}
int getval(int x,int l,int r,int id){
if(!id) return 0;
if(l==r) return l;
pushdown(l,r,id);
if(x<=sum[lt[id]]) return getval(x,l,mid,lt[id]);
else return getval(x-sum[lt[id]],mid+1,r,rt[id]);
}
void merge(int& id1,int id2,int l,int r){
if(!id1||!id2) id1+=id2;
else if(l==r) sum[id1]+=sum[id2],lg[id1]=sum[id1]*log10(l);
else{
pushdown(l,r,id1);
pushdown(l,r,id2);
merge(lt[id1],lt[id2],l,mid);
merge(rt[id1],rt[id2],mid+1,r);
pushup(id1);
}
}
}st;
class UFond{
private:
int fa[N];
public:
UFond(){for(int i=1;i<N;i++)fa[i]=i;}
int getf(int x){return x==fa[x]?x:fa[x]=getf(fa[x]);}
void uni(int x,int y){
int fx=getf(x),fy=getf(y);
if(fx==fy) return ;
fa[fy]=fx;
st.merge(st.ro[fx],st.ro[fy],1,MAX);
}
}uf;
signed main(){
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
freopen("out.out","w",stdout);
#endif
read(n);
for(int i=1;i<=n;i++){
int p,x,y;read(p,x);
if(p==1){
st.add(x,1,MAX,1,st.ro[++tot_]);
}else if(p==2){
read(y);
uf.uni(x,y);
}else if(p==3){
read(y);
if(y==1) continue;
int sum=st.getsum(1,y-1,1,MAX,st.ro[uf.getf(x)]);
if(sum==0) continue;
st.clr(1,y-1,1,MAX,st.ro[uf.getf(x)]);
st.add(y,1,MAX,sum,st.ro[uf.getf(x)]);
}else if(p==4){
read(y);
int sum=st.getsum(y+1,MAX,1,MAX,st.ro[uf.getf(x)]);
if(sum==0) continue;
st.clr(y+1,MAX,1,MAX,st.ro[uf.getf(x)]);
st.add(y,1,MAX,sum,st.ro[uf.getf(x)]);
}else if(p==5){
read(y);
write(st.getval(y,1,MAX,st.ro[uf.getf(x)]),'\n');
}else if(p==6){
read(y);
double a=st.lg[st.ro[uf.getf(x)]],b=st.lg[st.ro[uf.getf(y)]];
write(a>b?1:0,'\n');
}else{
write(st.getsum(1,MAX,1,MAX,st.ro[uf.getf(x)]),'\n');
}
}
return 0;
}
5.领导集团问题
线段树优化 dp。
定义 \(dp_{i,j}\) 表示节点到 \(i\) ,最小值 \(\ge j\) 时可以取到的最大数量。
易得(v 是 i 的子节点
因为显然,dp_i 是单调不升的,所以原式可以化简成:
然后对每个节点开一棵线段树,表示 \(dp_{i,j}\),相加的操作可以合并,\(\max\) 可以二分 \(+\) 区间推平(因为不升
CODE
#include<bits/stdc++.h>
using namespace std;
typedef long long llt;
#define For(i,a,b) for(int $L=a,$R=b,$D=($L<=$R)-($R<$L),i=$L;i*$D<=$R*$D;i+=$D)
#define Fcr(i,a,b,c) for(int $L=a,$R=b,$C=c,$D=(0<=$R-$L)-($R-$L<0),i=$L;i*$D<=$R*$D;i+=$D*$C)
const int N=2e5+3;
int w[N],n,MAX=1e9+1;
namespace IO{
template<typename T> inline void read(T &x){
char s=getchar();x=0;bool pd=false;
while(s<'0'||'9'<s){if(s=='-') pd=true;s=getchar();}
while('0'<=s&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
if(pd) x=-x;
}
template<typename T,typename... Args> inline void read(T& x,Args&... args){read(x);read(args...);}
template<typename T> inline void write(T x){
static T st[45];T top=0;
if(x<0)x=~x+1,putchar('-');
do{st[top++]=x%10;}while(x/=10);
while(top)putchar(st[--top]^48);
}
inline void write(const char c){putchar(c);}
inline void write(const char *c){
int len=strlen(c);
For(i,0,len-1) putchar(c[i]);
}
template<typename T> inline void Write(T x){write(x);putchar(' ');}
inline void Write(const char c){write(c);if(c!='\n') putchar(' ');}
inline void Write(const char *c){write(c),putchar(' ');}
template<typename T,typename... Args> inline void write(T x,Args... args){write(x);write(args...);}
template<typename T,typename... Args> inline void Write(T x,Args... args){Write(x);Write(args...);}
}
#define read IO::read
#define write IO::write
#define Write IO::Write
class LSNUM{
private:
int ls[N];
public:
void lsnum(){
For(i,1,n) ls[i]=w[i];
sort(ls+1,ls+n+1);
MAX=unique(ls+1,ls+n+1)-ls;
For(i,1,n) w[i]=lower_bound(ls+1,ls+MAX,w[i])-ls;
}
}LSnum;
class ST{
private:
int lt[N*40],rt[N*40],lz[N*40],tot_;
public:
#define mid ((l+r)>>1)
int ro[N*40];
void add(int L,int R,int l,int r,int x,int &id){
if(!id) id=++tot_;
// Write(L,R,l,r,x,'\n');
if(L<=l&&r<=R) lz[id]+=x;
else{
if(L<=mid) add(L,R,l,mid,x,lt[id]);
if(mid<R) add(L,R,mid+1,r,x,rt[id]);
}
}
int get(int t,int l,int r,int id){
if(!id) return 0;
else if(l==r) return lz[id];
else if(t<=mid) return get(t,l,mid,lt[id])+lz[id];
else return get(t,mid+1,r,rt[id])+lz[id];
}
void merge(int &id1,int id2,int l,int r){
if(!id1||!id2) id1+=id2;
else{
lz[id1]+=lz[id2];
merge(lt[id1],lt[id2],l,mid);
merge(rt[id1],rt[id2],mid+1,r);
}
}
#undef mid
}st;
class TO{
private:
int hd[N],nt[N<<1],to[N<<1],tot_;
public:
#define For_to(i,x) for(int i=hd[x];~i;i=nt[i])
TO(){memset(hd,-1,sizeof hd);}
inline void add(int x,int y){
to[tot_]=y,nt[tot_]=hd[x],hd[x]=tot_++;
}
void dfs(int x){
For_to(i,x){
int y=to[i];
dfs(y);
st.merge(st.ro[x],st.ro[y],1,MAX);
}
int chk=st.get(w[x],1,MAX,st.ro[x])+1;
int l=1,r=w[x];
while(l<=r){
int mid=(l+r)>>1;
if(st.get(mid,1,MAX,st.ro[x])>=chk) l=mid+1;
else r=mid-1;
}
st.add(l,w[x],1,MAX,1,st.ro[x]);
}
#undef For_to
}to;
int main(){
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
freopen("out.out","w",stdout);
#endif
read(n);
For(i,1,n) read(w[i]);
LSnum.lsnum();
for(int i=2;i<=n;i++){
int f;read(f);
to.add(f,i);
}
to.dfs(1);
Write(max(st.get(w[1],1,MAX,st.ro[1]),st.get(1,1,MAX,st.ro[1])));
}
6.大融合
首先,一个显然的性质,负载 \(=\) 左边的点数 \(\times\) 右边的点数
考虑离线
先求出dfs序,对于每个节点建一棵线段树,在其dfs序的位置 \(+1\)。
加边时,将两棵合并,用并查集维护。
查询时,求这条边两个深度较深的一个的子树大小,具体的,在联通快的线段树中查询dfs序中子树的区间的区间和,然后用联通快大小(\(1\) 到 \(N\) 的区间和) \(-\) 子树大小,就可以求出另一边。
最后相乘即可。
CODE
#include<bits/stdc++.h>
using namespace std;
typedef long long llt;
#define int llt
const int N=1e5+2,MAX=1e5+3;
#define For(i,a,b) for(int $L=a,$R=b,$D=($L<=$R)-($R<$L),i=$L;i*$D<=$R*$D;i+=$D)
#define Fcr(i,a,b,c) for(int $L=a,$R=b,$C=c,$D=(0<=$R-$L)-($R-$L<0),i=$L;i*$D<=$R*$D;i+=$D*$C)
struct question{
char p;int x,y;
}cq[N];
int n,q,din[N],dout[N],dtot_;
namespace IO{
template<typename T> inline void read(T &x){
char s=getchar();x=0;bool pd=false;
while(s<'0'||'9'<s){if(s=='-') pd=true;s=getchar();}
while('0'<=s&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
if(pd) x=-x;
}
template<typename T,typename... Args> inline void read(T& x,Args&... args){read(x);read(args...);}
template<typename T> inline void write(T x){
static T st[45];T top=0;
if(x<0)x=~x+1,putchar('-');
do{st[top++]=x%10;}while(x/=10);
while(top)putchar(st[--top]^48);
}
inline void write(const char c){putchar(c);}
inline void write(const char *c){
int len=strlen(c);
For(i,0,len-1) putchar(c[i]);
}
template<typename T> inline void Write(T x){write(x);putchar(' ');}
inline void Write(const char c){write(c);if(c!='\n') putchar(' ');}
inline void Write(const char *c){write(c),putchar(' ');}
template<typename T,typename... Args> inline void write(T x,Args... args){write(x);write(args...);}
template<typename T,typename... Args> inline void Write(T x,Args... args){Write(x);Write(args...);}
}
#define read IO::read
#define write IO::write
#define Write IO::Write
class ST{
private:
int lt[N*20],rt[N*20],sum[N*20],tg[N*20],tot_;
public:
int ro[N*20];
#define mid ((l+r)>>1)
#define Stg S+tg[id]
void add(int x,int l,int r,int t,int& id){
if(!id) id=++tot_;
sum[id]+=t;
if(l==r) tg[id]+=t;
else if(x<=mid) add(x,l,mid,t,lt[id]);
else add(x,mid+1,r,t,rt[id]);
}
int get(int L,int R,int l,int r,int S,int id){
if(!id) return 0;
else if(L==l&&R==r) return S*(R-L+1)+sum[id];
else if(R<=mid) return get(L,R,l,mid,Stg,lt[id]);
else if(L>mid) return get(L,R,mid+1,r,Stg,rt[id]);
else return get(L,mid,l,mid,Stg,lt[id])+get(mid+1,R,mid+1,r,Stg,rt[id]);
}
void merge(int& id1,int& id2,int l,int r){
if(!id1||!id2) id1+=id2;
else{
sum[id1]+=sum[id2],tg[id1]+=tg[id2];
merge(lt[id1],lt[id2],l,mid);
merge(rt[id1],rt[id2],mid+1,r);
}
}
#undef mid
#undef Stg
}st;
class TO{
private:
int hd[N],nt[N<<1],to[N<<1],tot_;
public:
#define For_to(i,x,y) for(int i=hd[x],y=to[i];~i;i=nt[i],y=to[i])
TO(){memset(hd,-1,sizeof hd);}
inline void add(int x,int y){
to[tot_]=y,nt[tot_]=hd[x],hd[x]=tot_++;
}
void dfs(int x){
din[x]=++dtot_;
For_to(i,x,y)
if(!din[y]) dfs(y);
dout[x]=dtot_;
}
#undef For_to
}to;
class UFOND{
private:
int fa[N];
public:
UFOND(){For(i,1,N)fa[i]=i;}
int get(int x){return x==fa[x]?x:fa[x]=get(fa[x]);}
void uni(int x,int y){
int fx=get(x),fy=get(y);
if(fx!=fy){
fa[fy]=fx;
st.merge(st.ro[fx],st.ro[fy],1,MAX);
}
}
}uf;
signed main(){
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
freopen("out.out","w",stdout);
#endif
read(n,q);
For(i,1,q){
scanf(" %c",&cq[i].p);
read(cq[i].x,cq[i].y);
if(cq[i].p=='A'){
to.add(cq[i].x,cq[i].y);
to.add(cq[i].y,cq[i].x);
}
}
For(i,1,n) if(!din[i]) to.dfs(i);
For(i,1,n) st.add(din[i],1,MAX,1,st.ro[i]);
For(i,1,q){
int x=cq[i].x,y=cq[i].y;
if(cq[i].p=='A') uf.uni(x,y);
else{
int fa=uf.get(x);
int la=st.get(1,MAX,1,MAX,0,st.ro[uf.get(fa)]);
int lb=min(st.get(din[x],dout[x],1,MAX,0,st.ro[uf.get(x)]),st.get(din[y],dout[y],1,MAX,0,st.ro[uf.get(y)]));
Write((la-lb)*lb,'\n');
}
}
// For(i,1,n) Write(din[i],dout[i],'\n');
// read(n);
// For(i,1,n){
// int p,x,y,id;read(p,x,y);
// if(p==1) read(id),st.add(x,1,MAX,y,st.ro[id]);
// else if(p==2) read(id),write(st.get(x,y,1,MAX,0,st.ro[id]),'\n');
// else st.merge(st.ro[x],st.ro[y],1,MAX);
// }
}
本文来自博客园,作者:xrlong,转载请注明原文链接:https://www.cnblogs.com/xrlong/articles/17536086.html
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。