一般用法:默一遍,提交代码,AC才过关
LCT https://www.luogu.com.cn/problem/P1501
重点:f[x]=c[y][0]=0;
#include<bits/stdc++.h>
#define R register unsigned int
#define I inline void
#define lc c[x][0]
#define rc c[x][1]
using namespace std;
const int N=1e5+10,mod=51061;
unsigned int f[N],c[N][2],val[N],s[N],sta[N],size[N],la[N],lm[N],top;
bool r[N];
int in(){
char c=getchar(); int op=0;
while(!isdigit(c)) c=getchar();
while(isdigit(c)) op=op*10+c-'0',c=getchar();
return op;
}
inline bool isroot(R x) { return (c[f[x]][0]!=x && c[f[x]][1]!=x); }
inline bool son(int x) { return x==c[f[x]][1]; }//判左右朝向
I update(R x) { s[x]=(s[lc]+s[rc]+val[x])%mod; size[x]=size[lc]+size[rc]+1; }
I reverse(R x) { if(x) swap(lc,rc),r[x]^=1; }
I add(R x,R k) { s[x]=(s[x]+k*size[x])%mod; val[x]=(val[x]+k)%mod; la[x]=(la[x]+k)%mod; }
I mul(R x,R k) { s[x]=s[x]*k%mod; la[x]=la[x]*k%mod; val[x]=val[x]*k%mod; lm[x]=lm[x]*k%mod; }
I pushdown(R x){
if(lm[x]!=1)mul(lc,lm[x]),mul(rc,lm[x]),lm[x]=1;
if(la[x]) add(lc,la[x]),add(rc,la[x]),la[x]=0;
if(r[x]) reverse(lc),reverse(rc),r[x]=0;
}
I rotate(R x){
int y=f[x],z=f[y];
if(!isroot(y)) c[z][son(y)]=x;//有爷爷就要更新 //x代替y
int a=son(x),w=c[x][!a];//我是y的左儿子,我的右儿子(反儿子、另一个儿子)变成y的左儿子
f[w]=y; c[y][a]=w;//x的另一个儿子代替x原来的位置
f[y]=x; c[x][!a]=y;
f[x]=z; update(y);
}
I splay(R x){
sta[top=1]=x;
for(R i=x;!isroot(i);i=f[i]) sta[++top]=f[i];
while(top) pushdown(sta[top--]);
for(R y=f[x];!isroot(x);rotate(x),y=f[x])
if(!isroot(y)) rotate(son(x)^son(y)?x:y);
update(x);
}
I access(R x) {for(R y=0;x;x=f[y=x])splay(x),rc=y,update(x);}
I makeroot(R x) {access(x);splay(x);reverse(x);}
I split(R x,R y) {makeroot(x);access(y);splay(y);}//将路径提到splay里
I link(R x,R y) {makeroot(x);f[x]=y;}
I cut(R x,R y) {split(x,y);f[x]=c[y][0]=0;}//y旋成根,x为y左儿子,所以左儿子变0
int main()
{
R n=in(),q=in(),x,y,c;
for(R i=1;i<=n;++i) val[i]=size[i]=lm[i]=1;
for(R i=1;i<n;i++) x=in(),y=in(),link(x,y);
while(q--){
char p[10];scanf("%s",p);
if(p[0]=='+'){
x=in(),y=in(),c=in();
split(x,y),add(y,c);
}
if(p[0]=='-'){
x=in(),y=in();cut(x,y);
x=in(),y=in();link(x,y);
}
if(p[0]=='*'){
x=in(),y=in(),c=in();
split(x,y),mul(y,c);
}
if(p[0]=='/'){
x=in(),y=in();
split(x,y),printf("%d\n",s[y]);
}
}
return 0;
}
树链剖分 改段 https://www.luogu.com.cn/problem/P2486#submit
重点:dep[top[x]]<dep[top[y]] 如果用tx ty 要 swap(x,y) swap(tx,ty)
#include<cstdio>
#include<cstring>
#define LL long long
#include<algorithm>
using namespace std;
const int N=100010;
int n,len=0,first[N];
struct bian{int y,gg;}b[N<<1];
void ins(int x,int y){
b[++len].y=y;
b[len].gg=first[x];
first[x]=len;
}
int dep[N],tot[N],son[N],fa[N];
void dfs(int x){
dep[x]=dep[fa[x]]+1; tot[x]=1;
for(int i=first[x];i>0;i=b[i].gg){
int y=b[i].y;
if(y!=fa[x]){
fa[y]=x;dfs(y);
if(!son[x] || tot[y]>tot[son[x]]) son[x]=y; tot[x]+=tot[y];
}
}
}
int fre[N],rev[N],top[N],trlen=0;
void dfs2(int x,int tp){
fre[x]=++trlen; top[x]=tp;
if(son[x]) dfs2(son[x],tp);
for(int i=first[x];i>0;i=b[i].gg){
int y=b[i].y;
if(y!=fa[x] && y!=son[x]) dfs2(y,y);
}
}//MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM//MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM//MMM
struct tree{int c,l,r,lz;}tr[N<<2];
void pushdown(int now){
if(tr[now].lz){
tr[now<<1].lz=tr[now<<1|1].lz=
tr[now<<1].l=tr[now<<1|1].l=
tr[now<<1].r=tr[now<<1|1].r=tr[now].lz;
tr[now<<1].c=tr[now<<1|1].c=1;
tr[now].lz=0;
}
}
void update(int now){
tr[now].c=tr[now<<1].c+tr[now<<1|1].c;
if(tr[now<<1].r && tr[now<<1|1].l && tr[now<<1].r==tr[now<<1|1].l) tr[now].c--;
tr[now].l=tr[now<<1].l;
tr[now].r=tr[now<<1|1].r;
}
void chan(int now,int l,int r,int x,int k)
{
if(l==r){
tr[now].l=tr[now].r=k;
tr[now].c=1;
// if(l==4) printf("%d",now);
return;
}
int mid=(l+r)>>1;
if(x<=mid) chan(now<<1,l,mid,x,k);
else chan(now<<1|1,mid+1,r,x,k);
update(now);
//printf("c=%d l%d r%d now%d\n",tr[now].c,tr[now].l,tr[now].r,now) ;
}
void change(int now,int l,int r,int L,int R,int k)
{
if(l>R || r<L) return ;
if(L<=l && r<=R){
tr[now].lz=tr[now].l=tr[now].r=k;
tr[now].c=1;
return;
}
int mid=(l+r)>>1;
pushdown(now);
change(now<<1,l,mid,L,R,k);
change(now<<1|1,mid+1,r,L,R,k);
update(now);
}
int dL,dR;
int minn(int x,int y ){ return x<y?x:y; }
int maxx(int x,int y ){ return x>y?x:y; }
int find(int now,int l,int r,int L,int R)
{
// printf("%d %d %d %d %d\n",now,l,r,L,R);
if(l==L) dL=tr[now].l;
if(r==R) dR=tr[now].r;
if(l>R || r<L) return 0;
if(L<=l && r<=R) {/*printf("\nn=%d %d\n",now,tr[now].c);*/return tr[now].c;}
int mid=(l+r)>>1;
pushdown(now);
int ll=find(now<<1,l,mid,L,R), rr=find(now<<1|1,mid+1,r,L,R);
return ll + rr + ((ll && rr && tr[now<<1].r==tr[now<<1|1].l)?-1:0); /*重中之重*/
}//MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM//MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM/
int fsolve(int x,int y)
{
int tx=top[x],ty=top[y],ans=0;
int la1=-1,la2=-1;
while(tx!=ty)
{
if(dep[tx]>dep[ty]) swap(tx,ty),swap(x,y),swap(la1,la2);
// printf("%d %d %d\n",fre[ty],fre[y],y);
dL=2147483647,dR=0;
ans+=find(1,1,trlen,fre[ty],fre[y]);
if(la2==dR) ans--;
la2=dL;
y=fa[ty];ty=top[y];
}
if(dep[x]>dep[y]) swap(x,y),swap(la1,la2);
dL=2147483647,dR=0;
ans+=find(1,1,trlen,fre[x],fre[y]);
if(dL==la1) ans--;
if(dR==la2) ans--;
return ans;
}
void csolve(int x,int y,int c)
{
int tx=top[x],ty=top[y];
while(tx!=ty)
{
if(dep[tx]>dep[ty]) swap(tx,ty),swap(x,y);
// printf("\nty%d y%d\n",ty,y);
change(1,1,trlen,fre[ty],fre[y],c);
y=fa[ty];ty=top[y];
}
if(dep[x]>dep[y]) swap(x,y);
change(1,1,trlen,fre[x],fre[y],c);
}//MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM//MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM/
int a[N],m;
int main(){
scanf("%d %d",&n,&m);
int x,y,c;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<n;i++){
scanf("%d %d",&x,&y);
ins(x,y);ins(y,x);
}
dfs(1);dfs2(1,1);
for(int i=1;i<=n;i++) chan(1,1,trlen,fre[i],a[i]);
char p[20];
for(int i=1;i<=m;i++){//printf("\n\n\n\n\n");
scanf("%s %d %d",p+1,&x,&y);
if(p[1]=='C') scanf("%d",&c),csolve(x,y,c);
else if(p[1]=='Q') printf("%d\n",fsolve(x,y));
}
return 0;
}
lca https://www.luogu.com.cn/problem/P3379
重点: f[x][i]!=f[y][i] 而不是 dep[f[x][i]]!=dep[f[y][i]]
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=500100;
int x[N],y[N],w[N];
struct node{int x,y,c;}a[N<<1];
bool cmp(node a,node b) { return a.c<b.c; }
bool v[N];
int len=0,first[N];
struct bian{int y,c,gg;}b[N<<1];
void ins(int x,int y){
b[++len].y=y;
// b[len].c=c;
b[len].gg=first[x];
first[x]=len;
}
int f[N][22],dep[N],m[N][22];
int maxx(int x,int y){ return x>y?x:y; }
void dfs(int x)
{
dep[x]=dep[f[x][0]]+1;
for(int i=1;i<=20;i++) f[x][i]=f[f[x][i-1]][i-1];//, M[x][i]=max(M[x][i-1],M[f[x][i-1]][i-1]);
for(int i=first[x];i>0;i=b[i].gg){
int y=b[i].y;
if(y!=f[x][0]){
f[y][0]=x;//M[x][0]=b[i].c;
dfs(y);
}
}
}
int l[N],R[N];
int lca(int x,int y){
int ans=0;
if(dep[x]>dep[y]) swap(x,y);
for(int i=20;i>=0;i--) if(dep[f[y][i]]>=dep[x]) /*ans=maxx(ans,m[y][i]),*/ y=f[y][i];
if(x==y) return x;//return ans;
for(int i=20;i>=0;i--) if(f[y][i]!=f[x][i])
/*ans=maxx(ans,m[y][i]),ans=maxx(ans,m[x][i]),*/y=f[y][i],x=f[x][i];
//ans=maxx(ans,m[x][0]),ans=maxx(ans,m[y][0]);return ans;
return f[x][0];
}
int main()
{
int n,Q,st,x,y,c;scanf("%d %d %d",&n,&Q,&st);
for(int i=1;i<n;i++) scanf("%d %d",&x,&y),ins(x,y),ins(y,x);
dfs(st);
for(int i=1;i<=Q;i++){
scanf("%d %d",&x,&y);
printf("%d\n",lca(x,y));
}
return 0;
}
扫描线计算面积 https://www.luogu.com.cn/problem/P5490
左闭右开为了处理a[r]-a[r]=0
看这样两条相邻线段:[1,2],[2,3]
你会发现[1,2]∩[2,3]={2},也就是说左儿子的右端点和右儿子的左端点其实是重合的。
所以右端点手动减1,计算时再用r+1计算
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
LL n;
LL X1[100100], Y1[100100], X2[100100], Y2[100100];
struct TR{
LL X,Y1,Y2,k;
TR(LL X=0,LL Y1=0,LL Y2=0,LL k=0) : X(X),Y1(Y1),Y2(Y2),k(k) { }
}line[200100];
bool cmp(TR a, TR b) { return a.X<b.X; }
long long tag[900100];
long long tree[900100],a[200100];
void push_down(LL now, LL l, LL r){
if(tag[now]) tree[now]=a[r+1]-a[l];//左闭右开
else tree[now]=tree[now<<1] + tree[now<<1|1];
}
void add(LL now,LL l,LL r,LL L,LL R,LL k) {//维护一个区间加,区间为正个数
if(R<l || r<L) return ;
if(L<=l && r<=R) {
tag[now]+=k;
push_down(now,l,r);
return ;
}
LL mid=(l+r)>>1;
add(now<<1, l,mid, L,R,k);
add(now<<1|1, mid+1,r, L,R,k);
push_down(now,l,r);
}
int main() {
scanf("%lld", &n);
LL tot=0;
for(LL i=1;i<=n;i++){
scanf("%lld %lld %lld %lld",&X1[i],&Y1[i],&X2[i],&Y2[i]);
a[++tot]=Y1[i];
line[tot]=TR(X1[i],Y1[i],Y2[i],1);//水平扫过去,到了这个左端加一排
a[++tot]=Y2[i];
line[tot]=TR(X2[i],Y1[i],Y2[i],-1);//到了右端减一排
}
sort(a+1,a+1+tot);
sort(line+1,line+1+tot,cmp);
LL m=unique(a+1,a+1+tot)-(a+1);
long long ans=0;
for(LL i=1;i<tot;i++) {
LL L=lower_bound(a+1, a+1+m, line[i].Y1) - a;
LL R=lower_bound(a+1, a+1+m, line[i].Y2) - a -1;///左闭右开
if(L<=R) add(1,1,m,L,R,line[i].k);
ans+=tree[1]*(line[i+1].X-line[i].X);//计算面积
}
printf("%lld\n", ans);
return 0;
}
三维偏序 排序+CDQ分治(归并版)+树状数组 https://www.luogu.com.cn/problem/P3810
#include<cstdio>
#include<algorithm>
#define I inline
#define R register int
using namespace std;
const int N=1e5+10;
int n,m;
int val[N],ans[N],cnt[N],tr[N<<1];
struct node{int a,b,c,p;}a[N],p[N],q[N];
I int lowbit(R x) { return x&(-x); }
I int ask(R i) { R v=0; for(;i;i-=lowbit(i)) v+=tr[i]; return v; }
I void change(R i,R v){ for(;i<=m;i+=lowbit(i)) tr[i]+=v; }
I void cdq(int l,int r)
{
if(l==r) return;
int mid=(l+r)>>1,i=l,j=mid+1;
cdq(l,mid),cdq(mid+1,r);
for(int k=l;k<=r;k++){
if((i<=mid && p[i].b<=p[j].b) || j>r) change(p[i].c,val[p[i].p]),q[k]=p[i++];
else cnt[p[j].p]+=ask(p[j].c),q[k]=p[j++];
}
for(int i=l;i<=mid;i++) change(p[i].c,-val[p[i].p]);
for(int i=l;i<=r;i++) p[i]=q[i];
}
inline bool cmp(node x,node y){
if(x.a==y.a){if(x.b==y.b) return x.c<y.c; else return x.b<y.b;}
else return x.a<y.a;
}
int main()
{
int tn;scanf("%d %d",&tn,&m);
for(int i=1;i<=tn;i++)
scanf("%d %d %d",&a[i].a,&a[i].b,&a[i].c);
sort(a+1,a+tn+1,cmp); n=0;
for(int i=1;i<=tn;i++){//去重
if(a[i].a^a[i-1].a || a[i].b^a[i-1].b || a[i].c^a[i-1].c) p[++n]=a[i],p[n].p=n;
val[n]++;
}
cdq(1,n);
for(int i=1;i<=n;i++)
ans[cnt[p[i].p]+val[p[i].p]-1]+=val[p[i].p];
for(int i=0;i<tn;i++) printf("%d\n",ans[i]);
return 0;
}
主席树 静态区间第k小 https://www.luogu.com.cn/problem/P3834#submit
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
int b[200100],c[200100],trlen=0;
struct node{int id,c;}a[200100];
struct tree{int lc,rc,c;}tr[4000100];//主席树本身
int root[200100];//维护前i个数有几个在当前值域内
int bt(int l,int r)//同时掌控搜索区间
{
int p=++trlen;
if(l==r) return p;
int mid=(l+r)>>1;
tr[p].lc=bt(l,mid); tr[p].rc=bt(mid+1,r);
return p;
}
int insert(int now,int l,int r,int x)
{
int p=++trlen;
tr[p]=tr[now];tr[p].c++;
if(l==r) return p;
int mid=(l+r)>>1;
if(x<=mid) tr[p].lc=insert(tr[now].lc,l,mid,x);
else tr[p].rc=insert(tr[now].rc,mid+1,r,x);
return p;
}
int find(int x,int y,int l, int r,int k)
{
if(l==r) return l;
int cnt=tr[tr[y].lc].c-tr[tr[x].lc].c;
//root[y]:插入1~y root[x]:插入1~x 相减显然是对应值域编号x~y 在此值域的个数
int mid=(l+r)>>1;
if(k<=cnt) return find(tr[x].lc,tr[y].lc,l,mid,k);
else return find(tr[x].rc,tr[y].rc,mid+1,r,k-cnt);
}
bool cmp(node a,node b) { return a.c<b.c; }
int main()
{
int n,m,len=0;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i].c),a[i].id=i;
sort(a+1,a+1+n,cmp);
b[a[1].id]=++len;c[len]=a[1].c;
for(int i=2;i<=n;i++) {//去重 +离散化
if(a[i].c!=a[i-1].c) c[++len]=a[i].c;
b[a[i].id]=len;
}
root[0]=bt(1,len);
for(int i=1;i<=n;i++)
root[i]=insert(root[i-1],1,len,b[i]);
int x,y,k;
for(int i=1;i<=m;i++){
scanf("%d %d %d",&x,&y,&k);
printf("%d\n",c[find(root[x-1],root[y],1,len,k)]);
}
return 0;
}
普通莫队
背板子重点:
1.分块按照n−−√sqrt{n}
2.不必按块枚举,询问区间属于哪一块的信息记录在结构体内排序
3.排序先按块升序,再按右端点升序
4. l=1,r=0 防止(1,1)无法记录,所以r=0
#include<cstdio>
#include<algorithm>
#include<cmath>
#define LL long long
using namespace std;
int a[51000],t[51000];//t[i]数i出现次数
struct node{int x,y,i,d;}st[51000]; //区间左右范围 ;属于哪个块 最大50000^2 25亿->long long
bool cmp(node a,node b)
{
if(a.d==b.d) return a.y<b.y;
return a.d<b.d;
}LL ans=0,an[51000];
int main()
{
int n,m,k;
scanf("%d %d %d",&n,&m,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int q=sqrt(n);
for(int i=1;i<=m;i++){
scanf("%d %d",&st[i].x,&st[i].y);
st[i].i=i, st[i].d= (st[i].x-1)/q + 1; //分块
}
sort(st+1,st+1+m,cmp);
int l=1,r=0; //防止1 1无法记录,所以r=0
for(int i=1;i<=m;i++)
{//直接左右端点移动即可,已经按排最坏n*sqrt(n)顺序
//左端点最多 m* sqrt(n) ,右端点每块最多移到低(即n) sqrt(n)*n
while(l>st[i].x) l--, t[a[l]]++, ans+=2*t[a[l]]-1; //(x)^2-(x-1)^2 =2*x-1
while(r<st[i].y) r++, t[a[r]]++, ans+=2*t[a[r]]-1;
while(l<st[i].x) t[a[l]]--, ans-=2*t[a[l]]+1, l++;
while(r>st[i].y) t[a[r]]--, ans-=2*t[a[r]]+1, r--;
an[st[i].i]=ans;
}for(int i=1;i<=m;i++) printf("%lld\n",an[i]);
return 0;
}
并查集
int find(int x){
if(f[x]==x) return x;
return f[x]=find(f[x]);
}
树套树
Splay