East China Normal University

队名

Name

Chinese:道生一,一生二,二生三,三生万物

English:One,Two,Three,AK

Members

XuLiang Zhu - Mathematics

Mengyun Chen - Computer Science

Jiadong Xie - Software Engineering

对拍

建立一个 test.sh 的文件

While true; do
    ./maker
    ./a
    ./b
    if diff 1.out 2.out; then echo “AC”;else break;
fi
done

终端编译命令

chmod +x test.sh
./test.sh

输入输出优化

#define LL long long  

using namespace std;
  
inline char nc(){
    /* 
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
    */return getchar();
}
  
inline void read(int &x){
  char c=nc();int b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
  
inline void read(LL &x){
  char c=nc();LL b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
 
inline void read(char &x){
  for (x=nc();!(x=='('||x==')');x=nc());
}

inline int read(char *s)
{
    char c=nc();int len=1;
    for(;!(c=='('||c==')');c=nc()) if (c==EOF) return 0;
    for(;(c=='('||c==')');s[len++]=c,c=nc());
    s[len++]='\0';
    return len-2;
}
int wt,ss[19];
inline void print(int x){
    if (x<0) x=-x,putchar('-'); 
    if (!x) putchar(48); else {
    for (wt=0;x;ss[++wt]=x%10,x/=10);
    for (;wt;putchar(ss[wt]+48),wt--);}
}
inline void print(LL x){
    if (x<0) x=-x,putchar('-');
    if (!x) putchar(48); else {for (wt=0;x;ss[++wt]=x%10,x/=10);for (;wt;putchar(ss[wt]+48),wt--);}
}

数据结构

hash

常用质数

1572869, 3145739, 6291469, 12582917, 25165843, 50331653
7881299347898369 原根 6
180143985094819841 原根 6
1945555039024054273 原根 5
4179340454199820289 原根 3

字符串哈希

不同长度的字符串哈希,需要加一维长度。

二维hash

#define seed 13131
#define seed1 1313
#define maxn 1005
typedef unsigned long long ull;
using namespace std;
ull p,Hash[maxn][maxn],Hash1[maxn][maxn];
char s[maxn][maxn],str[105][105];
int n,m,x,y;
ull getHash()
{
    ull a,b=0;
    for(int i=0;i<x;i++)
    {
        a=0;
        for(int j=0;j<y;j++)
            a=a*seed1+str[i][j];
        b=b*seed+a;
    }
    return b;
}
int getAns()
{
    ull base,a;
    int ans=0;
    base=1;
    for(int i=0;i<y;i++)
        base*=seed1;
    for(int i=0;i<n;i++)
    {
        a=0;
        for(int j=0;j<y;j++)
            a=a*seed1+s[i][j];
        Hash1[i][y-1]=a;
        for(int j=y;j<m;j++)
            Hash1[i][j]=Hash1[i][j-1]*seed1-s[i][j-y]*base+s[i][j];
    }
    base=1;
    for(int i=0;i<x;i++)
        base*=seed;
    for(int i=y-1;i<m;i++)
    {
        a=0;
        for(int j=0;j<x;j++)
            a=a*seed+Hash1[j][i];
        Hash[x-1][i]=a;
        if(a==p) ans++;
        for(int j=x;j<n;j++)
        {
            Hash[j][i]=Hash[j-1][i]*seed-Hash1[j-x][i]*base+Hash1[j][i];
            if(Hash[j][i]==p) ans++;
        }
    }
    return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
            scanf("%s",s[i]);
        scanf("%d%d",&x,&y);
        for(int i=0;i<x;i++)
            scanf("%s",str[i]);
        p=getHash();    //hash str 
        printf("%d\n",getAns());  //hash s 
    }
    return 0;
}

并查集

int Find(int x)
{
    return x==fa[x]?x:fa[x]=Find(fa[x]);
}

int main()
{
    p=Find(id[x]),q=Find(id[y]);
    fa[p]=q;
}

可持久化并查集

修改操作(镜像)

1 p q:合并p和q所在集合,如果已经在一个集合中,忽略此命令
2 p q:把p移动到q所在集合,如果已经在一个集合中,忽略此命令
3 p:输出p所在集合的元素个数和该集合所有元素之和

const int MAXN = 100010;
int n,m;
int father[MAXN];
int sum[MAXN],num[MAXN];
void init(){
    for(int i=1;i<=n;i++){
        father[i]=i+n;
        father[i+n]=i+n;
        sum[i+n]=i;
        num[i+n]=1;
    }
}
int find(int x){
    return father[x]!=x?father[x]=find(father[x]):x;
}
int main(){
    int mark,x,y;
    while(scanf("%d%d",&n,&m)!=EOF){
         init();
         for(register int i=0;i<m;i++){
             scanf("%d",&mark);
             if(mark==1){
                scanf("%d%d",&x,&y);
                int fx=find(x),fy=find(y);
                if(fx!=fy){
                    father[fx]=fy;
                    sum[fy]+=sum[fx];
                    num[fy]+=num[fx];
                }
             }else if(mark==2){
                scanf("%d%d",&x,&y);
                int fx=find(x),fy=find(y);
                if(fx!=fy){
                   father[x]=fy;
                   sum[fy]+=x,sum[fx]-=x;
                   num[fy]++,num[fx]--;
                }
             }else{
                 scanf("%d",&x);
                 int fx=find(x);
                 printf("%d %d\n",num[fx],sum[fx]);
             }
         }
    }
    return 0;
}

历史查询

1 a b 合并a,b所在集合
2 k 回到第k次操作之后的状态(查询算作操作)
3 a b 询问a,b是否属于同一集合,是则输出1否则输出0

#define N 2000005
int n,m,sz;
int root[N],ls[N],rs[N],v[N],deep[N];
void build(int &k,int l,int r){
    if(!k)k=++sz;
    if(l==r){v[k]=l;return;}
    int mid=(l+r)>>1;
    build(ls[k],l,mid);
    build(rs[k],mid+1,r);
}
void modify(int l,int r,int x,int &y,int pos,int val){
    y=++sz;
    if(l==r){v[y]=val;deep[y]=deep[x];return;}
    ls[y]=ls[x];rs[y]=rs[x];
    int mid=(l+r)>>1;
    if(pos<=mid)
        modify(l,mid,ls[x],ls[y],pos,val);
    else modify(mid+1,r,rs[x],rs[y],pos,val);
}
int query(int k,int l,int r,int pos){
    if(l==r)return k;
    int mid=(l+r)>>1;
    if(pos<=mid)return query(ls[k],l,mid,pos);
    else return query(rs[k],mid+1,r,pos);
}
void add(int k,int l,int r,int pos){
    if(l==r){deep[k]++;return;}
    int mid=(l+r)>>1;
    if(pos<=mid)add(ls[k],l,mid,pos);
    else add(rs[k],mid+1,r,pos);
}
int find(int k,int x){
    int p=query(k,1,n,x);
    return x==v[p]?p:find(k,v[p]);
}
int main(){
    int f,k,a,b;
    cin>>n>>m;
    build(root[0],1,n);
    for(register int i=1;i<=m;i++){
        cin>>f;
        if(f==1){
            root[i]=root[i-1];
            cin>>a>>b;
            int p=find(root[i],a),q=find(root[i],b);
            if(v[p]==v[q])continue;
            if(deep[p]>deep[q])swap(p,q);
            modify(1,n,root[i-1],root[i],v[p],v[q]);
            if(deep[p]==deep[q])add(root[i],1,n,v[q]);
        }else if(f==2){
            cin>>k;root[i]=root[k];
        }else if(f==3){
            root[i]=root[i-1];
            cin>>a>>b;
            int p=find(root[i],a),q=find(root[i],b);
            if(v[p]==v[q])cout<<1<<endl;
            else cout<<0<<endl;
        }
    }
    return 0;
}

带权并查集

#include<iostream>
#include<cstdio>
using namespace std;
const int N=1e5+5;
int fa[N],rnk[N];
void Init(int n)
{
    for(int i=0;i<=n;i++)
    {
        fa[i]=i;
        rnk[i]=0;
    }
}
int Find(int x)
{
    if(x==fa[x])return x;
    int temp=fa[x];
    fa[x]=Find(fa[x]);
    rnk[x]=rnk[x]+rnk[temp];
    return fa[x];
}
void Merge(int s,int x,int y)
{
    int rx=Find(x);
    int ry=Find(y);
    if(rx==ry)return  ;
    fa[rx]=ry;
    rnk[rx]=s+rnk[y]-rnk[x];
}
int main()
{
    int n,m,q;
    cin>>n>>m>>q;   //人数,关系数,询问数
    Init(n);
    while(m--)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);   //a比b高c分
        Merge(c,a,b);
    }
    while(q--)
    {
        int a,b;
        scanf("%d%d",&a,&b);  //想知道a号同学的分数比b号同学的分数高几分
        int ra=Find(a);
        int rb=Find(b);
        if(ra!=rb)printf("-1\n");
        else printf("%d\n",rnk[a]-rnk[b]);  
    }
    return 0;
}

线段树

区间加乘,区间和

const int N=100005;
int n,m,a[4*N];
LL tr[4*N],add[4*N],mul[4*N],p;

void update(int x)
{
    tr[x]=(tr[x<<1]+tr[x<<1|1])%p;
}

void build(int now,int l,int r)
{
    mul[now]=1;add[now]=0;
    if (l==r){tr[now]=a[l];mul[now]=1;return;}
    int mid=l+r>>1;
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    update(now);
}

void pushdown(int now,int l,int r)
{
    int mid=l+r>>1;
    if (mul[now]!=1)
    {
        tr[now<<1]=tr[now<<1]*mul[now]%p;
        tr[now<<1|1]=tr[now<<1|1]*mul[now]%p;
        mul[now<<1]=mul[now<<1]*mul[now]%p;
        mul[now<<1|1]=mul[now<<1|1]*mul[now]%p;
        add[now<<1]=add[now<<1]*mul[now]%p;
        add[now<<1|1]=add[now<<1|1]*mul[now]%p;
        mul[now]=1;
    }
    if (add[now])
    {
        tr[now<<1]+=add[now]*(mid-l+1)%p;
        if (tr[now]>=p) tr[now]-=p;
        tr[now<<1|1]+=add[now]*(r-mid)%p;
        if (tr[now]>=p) tr[now]-=p;
        add[now<<1]+=add[now];
        if (add[now]>=p) add[now]-=p;
        add[now<<1|1]+=add[now]; 
        if (add[now]>=p) add[now]-=p;
        add[now]=0;
    }
}

void qjmul(int now,int l,int r,int ll,int rr,LL val)
{
    if (ll<=l&&r<=rr)
    {
        tr[now]=tr[now]*val%p;
        mul[now]=mul[now]*val%p;
        add[now]=add[now]*val%p;
        return;
    }
    int mid=l+r>>1;
    pushdown(now,l,r);
    if (ll<=mid) qjmul(now<<1,l,mid,ll,rr,val);
    if (rr>mid) qjmul(now<<1|1,mid+1,r,ll,rr,val);
    update(now);
}

void qjadd(int now,int l,int r,int ll,int rr,LL val)
{
    if (ll<=l&&r<=rr)
    {
        tr[now]=(LL)(tr[now]+(LL)(r-l+1)*val%p);
        if (tr[now]>=p) tr[now]-=p;
        add[now]+=val;
        if (add[now]>=p) add[now]-=p;
        return;
    }
    int mid=l+r>>1;
    pushdown(now,l,r);
    if (ll<=mid) qjadd(now<<1,l,mid,ll,rr,val);
    if (rr>mid) qjadd(now<<1|1,mid+1,r,ll,rr,val);
    update(now);
}

LL qjsum(int now,int l,int r,int ll,int rr)
{
    if (ll<=l&&r<=rr) return tr[now];
    int mid=l+r>>1; LL ans=0;
    pushdown(now,l,r);
    if (ll<=mid) ans+=qjsum(now<<1,l,mid,ll,rr); ans%=p;
    if (rr>mid) ans+=qjsum(now<<1|1,mid+1,r,ll,rr); ans%=p;
    return ans;
}

int main()
{
    read(n),read(m),read(p); 
    for (int i=1;i<=n;i++)
        read(a[i]);
    build(1,1,n);
    for (int i=1;i<=m;i++)
    {
        int opt,x,y,z;
        read(opt);read(x);read(y);
        if (opt==2) read(z),qjadd(1,1,n,x,y,z);
        if (opt==1) read(z),qjmul(1,1,n,x,y,z);
        if (opt==3) print(qjsum(1,1,n,x,y)%p),puts("");
    }
    return 0;
}

区间覆盖,询问

1 a:询问是不是有连续长度为 a 的空房间,有的话住进最左边
2 a b:将[a,a+b-1]的房间清空

struct ST
{
    int l,r,lc,rc,lf,rf,f,flag;
}a[200010];
int n,m;

void build(int l,int r,int x)
{
    a[x].l=l,a[x].r=r;a[x].flag=0;
    a[x].lf=a[x].rf=a[x].f=r-l+1;
    if (l==r) {a[x].lc=a[x].rc=0;return ;}
    int mid=(l+r)>>1;
    build(l,mid,x*2);
    build(mid+1,r,x*2+1);
    a[x].lc=x*2;a[x].rc=x*2+1;
}

void Pushdown(int x)
{
    if (a[x].flag==1)
    {
        a[x].flag=0;
        a[a[x].lc].f=a[a[x].lc].lf=a[a[x].lc].rf=0;
        if (a[a[x].lc].r!=a[a[x].lc].l) a[a[x].lc].flag=1;
        a[a[x].rc].f=a[a[x].rc].lf=a[a[x].rc].rf=0;
        if (a[a[x].rc].r!=a[a[x].rc].l) a[a[x].rc].flag=1;
    }
    if (a[x].flag==-1)
    {
        a[x].flag=0;
        a[a[x].lc].f=a[a[x].lc].lf=a[a[x].lc].rf=a[a[x].lc].r-a[a[x].lc].l+1;
        if (a[a[x].lc].r!=a[a[x].lc].l) a[a[x].lc].flag=-1;
        a[a[x].rc].f=a[a[x].rc].lf=a[a[x].rc].rf=a[a[x].rc].r-a[a[x].rc].l+1;
        if (a[a[x].rc].r!=a[a[x].rc].l) a[a[x].rc].flag=-1;
    }
}

int query(int k,int x)
{
    if (a[x].lf>=k) return a[x].l;
    Pushdown(x);
    if (a[a[x].lc].f>=k) return query(k,a[x].lc);
    if (a[a[x].lc].rf+a[a[x].rc].lf>=k) return a[a[x].lc].r-a[a[x].lc].rf+1;
    return query(k,a[x].rc);
}

void change(int l,int r,int x,int z)
{
    if (l<=a[x].l && r>=a[x].r)
    {
        if (a[x].l==a[x].r)
        {
            if (z==1) a[x].f=a[x].rf=a[x].lf=0;
            else a[x].f=a[x].rf=a[x].lf=1;
        }
        else
        {
            if (z==1) a[x].flag=1,a[x].f=a[x].lf=a[x].rf=0;
            else a[x].flag=-1,a[x].f=a[x].lf=a[x].rf=a[x].r-a[x].l+1;
        }
        return ;
    }
    Pushdown(x);
    int mid=(a[x].l+a[x].r)>>1;
    if (l<=mid) change(l,r,x*2,z);
    if (r>mid) change(l,r,x*2+1,z);
    a[x].f=max(a[a[x].lc].f,a[a[x].rc].f);
    a[x].f=max(a[x].f,a[a[x].lc].rf+a[a[x].rc].lf);
    if (a[a[x].lc].lf==a[a[x].lc].r-a[a[x].lc].l+1) a[x].lf=a[a[x].lc].lf+a[a[x].rc].lf;
    else a[x].lf=a[a[x].lc].lf;
    if (a[a[x].rc].rf==a[a[x].rc].r-a[a[x].rc].l+1) a[x].rf=a[a[x].rc].rf+a[a[x].lc].rf;
    else a[x].rf=a[a[x].rc].rf;
}

int main()
{
    read(n);read(m);
    build(1,n,1);
    int x,y;
    while (m--)
    {
        read(x);
        if (x==1)
        {
            read(y);
            if (a[1].f<y) print(0),putchar('\n');
            else
            {
                int ans=query(y,1);
                print(ans),putchar('\n');
                change(ans,ans+y-1,1,1);
            }
        }
        else
        {
            read(x);read(y);
            change(x,x+y-1,1,-1);
        }
    }
    return 0;
}

李超树

#define maxn 50010
int T,n,m;
double ans;
char s[50];
struct ST
{
    double k,b;
    bool flag;
}tree[4*maxn];

double cross(double k1,double b1,double k2,double b2)   //计算交点(需要注意斜率相等的情况)
{
    return (b2-b1)/(1.0*(k1-k2));
}
 
void Insert(int x,int l,int r,double B,double K)
{
    if (!tree[x].flag) {tree[x].k=K,tree[x].b=B,tree[x].flag=1;return;} 
    int mid=l+r>>1;
    double f1=1.0*K*l+B,f2=1.0*tree[x].k*l+tree[x].b,f3=1.0*K*r+B,f4=1.0*tree[x].k*r+tree[x].b;
    if (f1<=f2&&f3<=f4) return;
    if (f1>=f2&&f3>=f4) {tree[x].k=K,tree[x].b=B;return;} 
    double xx=cross(K,B,tree[x].k,tree[x].b);
    if (f1>=f2)
    {
        if (xx<=mid) Insert(x<<1,l,mid,B,K);
        else Insert(x<<1|1,mid+1,r,tree[x].b,tree[x].k),tree[x].k=K,tree[x].b=B;
    }
    else
    {
        if (xx>mid) Insert(x<<1|1,mid+1,r,B,K);
        else Insert(x<<1,l,mid,tree[x].b,tree[x].k),tree[x].k=K,tree[x].b=B;
    }
}

void query(int x,int l,int r,int q)
{
    if (tree[x].flag)  ans=max(ans,1.0*tree[x].k*q+tree[x].b);    //这里不能return,要走到底 
    if (l==r) return ;
    int mid=l+r>>1;
    if (q<=mid)query(x<<1,l,mid,q);else query(x<<1|1,mid+1,r,q);
}

int main()
{
    n=50000;
    read(T);
    while (T--)
    {
        int x=read(s);double B,K;
        if (s[1]=='P') scanf("%lf%lf",&B,&K),Insert(1,1,n,B-K,K);    //一次函数第一天为B,公差为K 
        else read(x),ans=0,query(1,1,n,x),print((LL)floor(ans/100.0)),puts("");   //询问第x天函数max 
    }
    return 0;
}

线段树的合并

求子树中有几个节点的权值小于根结点的权值

dfs,回溯的时候合并子树所构成的线段树

const int N=100010,M=N*20;
int n,a[N],ans[N],root[N],disc[N];
vector<int> v[N];
namespace Segment_Tree{
    int tot;
    struct node{int l,r,a,b,sum;}T[M];
    void up(int x){T[x].sum=T[T[x].l].sum+T[T[x].r].sum;}
    int build(int l,int r,int p){
        int x=++tot;
        T[x].a=l; T[x].b=r; T[x].sum=0;
        if(l==r){T[x].sum=1;return x;}
        int mid=(l+r)>>1;
        if(p<=mid){T[x].l=build(l,mid,p);}
        else{T[x].r=build(mid+1,r,p);}
        return up(x),x;
    }
    int ask(int x,int l,int r){
        if(!x)return 0;
        if(l<=T[x].a&&T[x].b<=r)return T[x].sum;
        int mid=(T[x].a+T[x].b)>>1,res=0;
        if(l<=mid)res+=ask(T[x].l,l,r);
        if(r>mid)res+=ask(T[x].r,l,r);
        return res;
    }
    int merge(int x,int y){
        if(!x||!y)return x^y;
        T[x].l=merge(T[x].l,T[y].l);
        T[x].r=merge(T[x].r,T[y].r);
        return up(x),x;
    }
    void dfs(int x,int fx){
        int res=0;
        for(int i=0;i<v[x].size();i++){
            int y=v[x][i];
            if(y==fx)continue;
            dfs(y,x);
            res+=ask(root[y],a[x]+1,n);
            root[x]=merge(root[x],root[y]);
        }ans[x]=res;
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),disc[i]=a[i];
    sort(disc+1,disc+n+1);
    int m=unique(disc+1,disc+n+1)-disc-1;
    for(int i=1;i<=n;i++)a[i]=lower_bound(disc+1,disc+m+1,a[i])-disc;
    for(int i=2;i<=n;i++){
        int x; scanf("%d",&x);
        v[x].push_back(i); v[i].push_back(x);
    }
    for(int i=1;i<=n;i++)root[i]=Segment_Tree::build(1,n,a[i]);
    Segment_Tree::dfs(1,1);
    for(int i=1;i<=n;i++)printf("%d\n",ans[i]);
    return 0;
}

线段树合并与拆分

1:(0,l,r)表示将区间[l,r]的数字升序排序

2:(1,l,r)表示将区间[l,r]的数字降序排序

最后询问第q位置上的数字。

#include<iostream>  
#include<queue>  
using namespace std;  
#define mid ((l+r)>>1)  
#define ls t<<1,l,mid  
#define rs t<<1|1,mid+1,r  
#define ND 2000010  
#define mxn 100010  
int n,m,k,x,tt,val,i,last,op,L,R,now,nxt,LEFT,RIGHT;  
struct data{  
    int l,r,tp,nxt;  
}c[ND];  
struct segment_tree{  
    struct data{  
        bool exi;  
        int rmax;  
    }a[mxn<<2];  
    void update(int t){  
        a[t]=(data){  
            a[t<<1].exi||a[t<<1|1].exi,  
            a[t<<1|1].rmax?a[t<<1|1].rmax:a[t<<1].rmax  
        };  
    }  
    void ins(int t,int l,int r){  
        if (l==r) a[t]=(data){k?1:0,k}; else  
        if (x<=mid) ins(ls),update(t); else  
        ins(rs),update(t);  
    }  
    int ask(int t,int l,int r){  
        if (l>x||(!a[t].exi)) return 0;  
        if (l==r) return a[t].rmax;  
        return (tt=ask(rs))?tt:(mid<=x?a[t<<1].rmax:ask(ls));  
    }  
}T;  
queue <int> q;  
int newnode(){  
    int k=q.front();  
    q.pop();  
    return k;  
}  
struct node{  
    int l,r,sum;  
}a[ND];  
void clear(int t){  
    a[t]=a[0];  
    q.push(t);  
}  
void build(int t,int l,int r){  
    a[t].sum=1;  
    if (l==r) return;  
    if (val<=mid) build(a[t].l=newnode(),l,mid);  
    else build(a[t].r=newnode(),mid+1,r);  
}  
int merge(int t1,int t2){  
    if (t1==0||t2==0) return t1+t2;  
    a[t1].l=merge(a[t1].l,a[t2].l);  
    a[t1].r=merge(a[t1].r,a[t2].r);  
    a[t1].sum+=a[t2].sum;  
    clear(t2);  
    return t1;  
}  
void split(int t1,int t2,int k){  
    int tt=a[a[t1].l].sum;  
    if (k>tt) split(a[t1].r,a[t2].r=newnode(),k-tt); else swap(a[t2].r,a[t1].r);  
    if (k<tt) split(a[t1].l,a[t2].l=newnode(),k);  
    a[t2].sum=a[t1].sum-k;  
    a[t1].sum=k;  
}  
int ask(int t,int l,int r,int k){  
    if (l==r) return l;  
    int tt=a[a[t].l].sum;  
    if (k>tt) return ask(a[t].r,mid+1,r,k-tt);  
    else return ask(a[t].l,l,mid,k);  
}  
int main(){  
    for (i=1;i<=ND-10;i++)q.push(i);  
    cin>>n>>m;  
    for (x=1,last=ND-5;x<=n;last=k,x++){  
        cin>>val;  
        c[k=c[last].nxt=newnode()]=(data){x,x,0,0};  
        T.ins(1,1,n);  
        build(k,1,n);  
    }  
    while (m--){  
        cin>>op>>L>>R;  
        x=L;  
        LEFT=T.ask(1,1,n);  
        if (L==c[LEFT].l){  
            RIGHT=newnode();  
            swap(a[RIGHT],a[LEFT]);  
            swap(c[RIGHT],c[LEFT]);  
            c[now=LEFT]=(data){L,R,op,RIGHT};  
        } else{  
            c[now=newnode()]=(data){L,R,op,RIGHT=newnode()};  
            if (!c[LEFT].tp) split(LEFT,RIGHT,L-c[LEFT].l);  
            else split(LEFT,RIGHT,c[LEFT].r-L+1),swap(a[LEFT],a[RIGHT]);  
            c[RIGHT]=c[LEFT];  
            c[RIGHT].l=L;  
            c[LEFT].r=L-1;  
            c[LEFT].nxt=now;  
        }  
        for (nxt=RIGHT;nxt&&R>=c[nxt].r;last=nxt,nxt=c[nxt].nxt,c[last]=c[0]){  
            merge(now,nxt);  
            x=c[nxt].l,k=0;  
            T.ins(1,1,n);  
        }  
        c[now].nxt=nxt;  
        if (nxt!=0&&c[nxt].l<=R){  
            x=c[nxt].l,k=0;  
            T.ins(1,1,n);  
            if (c[nxt].tp) split(nxt,tt=newnode(),c[nxt].r-R);  
            else split(nxt,tt=newnode(),R-c[nxt].l+1),swap(a[nxt],a[tt]);  
            merge(now,tt);  
            x=c[nxt].l=R+1;  
            k=nxt;  
            T.ins(1,1,n);  
        }  
        x=L,k=now;  
        T.ins(1,1,n);  
    }  
    cin>>x;  
    now=T.ask(1,1,n);  
    if (c[now].tp) cout<<ask(now,1,n,c[now].r-x+1)<<"\n";  
    else cout<<ask(now,1,n,x-c[now].l+1)<<"\n";  
    return 0;  
} 

权值线段树中位数查询

离散化后一共有 m 个元素,支持以下操作:

1.ins c d e:插入一个离散化后是 c 的元素,对 cnt 的贡献为 d,对 sum 的贡献为 e。

2.ask:查询所有数字与中位数的差值的绝对值的和。

struct ST
{
    int v[N];LL sum[N];
    void ins(int c,int d,int e)
    {
        int a=1,b=m,x=1,mid;
        while(1)
        {
            v[x]+=d,sum[x]+=e;
            if(a==b)return;
            x<<=1;
            if(c<=(mid=(a+b)>>1))b=mid;else x|=1,a=mid+1;
        }
    }
    LL ask()
    {
        if(v[1]<=1)return 0;
        int a=1,b=m,mid,t,k=(v[1]+1)/2,x=1,cnt=0;LL ans=0;
        while(a<b)
        {
            mid=(a+b)>>1,t=v[x<<=1];
            if(k<=t)cnt+=v[x|1],ans+=sum[x|1],b=mid;
            else cnt−=t,ans−=sum[x],k−=t,a=mid+1,x|=1;
        }
        return ans−sum[x]/v[x]*cnt;
    }
};

二维线段树

1.将子矩阵x1,y1,x2,y2所有元素加上V

2.将子矩阵x1,y1,x2,y2所有元素设为V

3查询将子矩阵x1,y1,x2,y2的和,最大,小值

#define inf 0x3f3f3f3f
int ans,MX,MN;
#define lson L,mid,(rt<<1)
#define rson mid+1,R,(rt<<1|1)
struct point
{
    int lc,rc,sum,mx,mn,add,set;
    int mid(){return (lc+rc)>>1;}
};
point **tree;
void init(int r,int c)
{
    tree=new point*[r+1];
    for(int i=0;i<=r;i++)
    {
        tree[i]=new point[c*4];
    }
}
void deal(int r,int c)
{
    for(int i=0;i<=r;i++)
    {
        delete tree[i];
    }
    delete tree;
}
void pushup(int r,int rt)
{
    tree[r][rt].sum=tree[r][rt<<1].sum+tree[r][rt<<1|1].sum;
    tree[r][rt].mx=max(tree[r][rt<<1].mx,tree[r][rt<<1|1].mx);
    tree[r][rt].mn=min(tree[r][rt<<1].mn,tree[r][rt<<1|1].mn);
}
void pushdown(int r,int rt)
{
    int lg=tree[r][rt].rc-tree[r][rt].lc+1;
    int rset,radd;
    rset=tree[r][rt].set;
    radd=tree[r][rt].add;
    if(tree[r][rt].set)
    {
        tree[r][rt<<1].sum=rset*(lg-(lg>>1));
        tree[r][rt<<1|1].sum=rset*(lg>>1);
 
        tree[r][rt<<1].mx=rset;
        tree[r][rt<<1].mn=rset;
        tree[r][rt<<1|1].mx=rset;
        tree[r][rt<<1|1].mn=rset;
 
        tree[r][rt<<1].set=rset;
        tree[r][rt<<1|1].set=rset;
 
        tree[r][rt<<1].add=0;
        tree[r][rt<<1|1].add=0;
 
        tree[r][rt].set=0;
    }
    if(tree[r][rt].add)
    {
        tree[r][rt<<1].sum+=radd*(lg-(lg>>1));
        tree[r][rt<<1|1].sum+=radd*(lg>>1);
 
        tree[r][rt<<1].mx+=radd;
        tree[r][rt<<1].mn+=radd;
        tree[r][rt<<1|1].mx+=radd;
        tree[r][rt<<1|1].mn+=radd;
 
        tree[r][rt<<1].add+=radd;
        tree[r][rt<<1|1].add+=radd;
        tree[r][rt].add=0;
    }
}
void build(int r,int L,int R,int rt)
{
    tree[r][rt].add=0;
    tree[r][rt].set=0;
    tree[r][rt].lc=L;
    tree[r][rt].rc=R;
    if(L==R)
    {
        tree[r][rt].sum=0;
        tree[r][rt].mx=0;
        tree[r][rt].mn=0;
        return ;
    }
    int mid=tree[r][rt].mid();
    build(r,lson);
    build(r,rson);
    pushup(r,rt);
}
void update_set(int r,int L,int R,int rt,int v)
{
    if(L==tree[r][rt].lc&&tree[r][rt].rc==R)
    {
        tree[r][rt].add=0;
        tree[r][rt].set=v;
        tree[r][rt].sum=(R-L+1)*v;
        tree[r][rt].mx=v;
        tree[r][rt].mn=v;
        return ;
    }
    pushdown(r,rt);
    int mid=tree[r][rt].mid();
    if(R<=mid)update_set(r,L,R,rt<<1,v);
    else if(L>mid)update_set(r,L,R,rt<<1|1,v);
    else
    {
        update_set(r,lson,v);
        update_set(r,rson,v);
    }
    pushup(r,rt);
}
void update_add(int r,int L,int R,int rt,int v)
{
    if(L==tree[r][rt].lc&&tree[r][rt].rc==R)
    {
        tree[r][rt].add+=v;
        tree[r][rt].sum+=(R-L+1)*v;
        tree[r][rt].mn+=v;
        tree[r][rt].mx+=v;
        return ;
    }
    pushdown(r,rt);
    int mid=tree[r][rt].mid();
    if(R<=mid)update_add(r,L,R,rt<<1,v);
    else if(L>mid)update_add(r,L,R,rt<<1|1,v);
    else
    {
        update_add(r,lson,v);
        update_add(r,rson,v);
    }
    pushup(r,rt);
}
void query(int r,int L,int R,int rt)
{
    if(L==tree[r][rt].lc&&tree[r][rt].rc==R)
    {
        ans+=tree[r][rt].sum;
        MX=max(MX,tree[r][rt].mx);
        MN=min(MN,tree[r][rt].mn);
        return ;
    }
    pushdown(r,rt);
    int mid=tree[r][rt].mid();
    if(R<=mid)query(r,L,R,rt<<1);
    else if(L>mid)query(r,L,R,rt<<1|1);
    else
    {
        query(r,lson);
        query(r,rson);
    }
}
int main()
{
    int r,c,m,k,x1,x2,y1,y2,v;
    while(~scanf("%d%d%d",&r,&c,&m))
    {
        init(r,c);
        for(int i=1;i<=r;i++)
        {
            build(i,1,c,1);
        }
        while(m--)
        {
            scanf("%d",&k);
            if(k==1)
            {
                scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&v);
                for(int i=x1;i<=x2;i++)
                {
                    update_add(i,y1,y2,1,v);
                }
            }
            else if(k==2)
            {
                scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&v);
                for(int i=x1;i<=x2;i++)
                {
                    update_set(i,y1,y2,1,v);
                }
            }
            else 
            {
                ans=0;
                MX=-inf;
                MN=inf;
                scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
                for(int i=x1;i<=x2;i++)
                {
                    query(i,y1,y2,1);
                }
                printf("%d %d %d\n",ans,MN,MX);
            }
        }
        deal(r,c);
    }
    return 0;
}

笛卡尔树

最大值为根的笛卡尔树

#define MAXN 1000010
const int inf=0x3f3f3f3f;

stack<int>st;
int T,n;
struct data
{
    int val,sz;//val满足堆,sz子树大小 
    int l,r,par;//左孩子,右孩子,父亲 
}t[MAXN];

void init()
{
    for(int i=0;i<=n;i++)
        t[i].l=0,t[i].r=0,t[i].par=0,t[i].sz=0;//初始化
    t[0].val=inf;
    while(!st.empty()) st.pop();
    st.push(0);
}

void build()
{
    for(int i=1;i<=n;i++)
    {
        while(!st.empty()&&t[st.top()].val<t[i].val)//从栈顶往栈底遍历
            st.pop();
        int par=st.top();
        t[i].par=par;
        t[i].l=t[par].r;
        t[t[par].r].par=i;
        t[par].r=i;
        st.push(i);
    }
}

int main()
{
    read(T);
    while(T--)
    {
        read(n);
        init();
        for(int i=1;i<=n;i++)
            read(t[i].val);
        build();
    }
}

树状数组

树上二分(一个log)

void change(int i,int x)
{
    for(;i<=n;i+=i&(-i)) c[i]+=x; 
}

int query(int i)
{
    int s=0;
    for (;i;i-=i&(-i)) s+=c[i];
    return s;
}

int qry(int x)   //i是sum[i]<x的最后一位 
{
    int i=0,ii,k,t=0,tt;
    for (k=tot>>1;k;k>>=1)
        if ((tt=t+c[ii=(i|k)])<x){t=tt;i=ii;}
    return i+1;
}

区间修改,区间询问

void add(LL p,LL x)
{
    for(int i=p;i<=n;i+=i&-i)
        sum1[i]+=x,sum2[i]+=x*p;
}

void range_add(LL l,LL r,LL x)
{
    add(l,x),add(r+1,-x);
}

LL ask(LL p)
{
    LL res=0;
    for(int i=p;i;i-=i&-i) res+=(p+1)*sum1[i]-sum2[i];
    return res;
}

LL range_ask(LL l, LL r)
{
    return ask(r)-ask(l-1);
}

三维树状数组

单点增,区间和

const int maxn = 128 + 10;
int N;
int c[maxn][maxn][maxn];
int lowerbit(int x){return x & (-x);}
void add(int x, int y, int z, int v)
{
    for(int i = x; i <= N; i += lowerbit(i))
        for(int j = y; j <= N; j += lowerbit(j))
            for(int k = z; k <= N; k += lowerbit(k))
                c[i][j][k] += v;
}
long long sum(int x, int y, int z)
{
    long long ret = 0;
    for(int i = x; i > 0; i -= lowerbit(i))
        for(int j = y; j > 0; j -= lowerbit(j))
            for(int k = z; k > 0; k -= lowerbit(k))
                ret += c[i][j][k];
    return ret;
}
int main()
{
    int op;
    while(scanf("%d", &N) == 1)
    {
        while(scanf("%d", &op) && op != 3)
        {
            if(op == 1)
            {
                int x, y, z, k;  
                scanf("%d%d%d%d", &x, &y, &z, &k);  
                add(x+1, y+1, z+1, k);  
            }
            else
            {
                int x1, y1, z1, x2, y2, z2; 
                scanf("%d%d%d%d%d%d", &x1, &y1, &z1, &x2, &y2, &z2);  
                x1++; y1++; z1++;  
                x2++; y2++; z2++;  
                printf("%I64d\n", sum(x2, y2, z2)-sum(x2, y1-1, z2)-sum(x1-1, y2, z2)+sum(x1-1, y1-1, z2)  
                       -(sum(x2, y2, z1-1)-sum(x2, y1-1, z1-1)-sum(x1-1, y2, z1-1)+sum(x1-1, y1-1, z1-1)));  
            }
        }
    }
    return 0;  
}

主席树

区间k大数

int T,n,m,s,k,h[100010],b[100010],d[100010];
struct ST
{
    int lc,rc,sum;
}a[2000010];

int Hash(int x)  
{  
    return lower_bound(b+1,b+1+k,x)-b;
}  

void build(int l,int r,int x)
{
    s++;x=s;
    if (l==r) {a[x].lc=a[x].rc=0;return ;}
    int mid=(l+r)>>1;
    a[x].lc=s+1;build(l,mid,x);
    a[x].rc=s+1;build(mid+1,r,x);
}

void change(int x,int y,int z,int xx,int l,int r)     //新结点编号,,,对应结点编号,[l,r]
{
    if (l==r) {a[x].sum=a[xx].sum+z;return ;}
    int mid=(l+r)>>1;
    if (y<=mid)
    {
        a[x].rc=a[xx].rc;
        s++;a[x].lc=s;
        change(s,y,z,a[xx].lc,l,mid);
    }
    else
    {
        a[x].lc=a[xx].lc;
        s++;a[x].rc=s;
        change(s,y,z,a[xx].rc,mid+1,r);
    }
    a[x].sum=a[a[x].lc].sum+a[a[x].rc].sum;
}

int query(int x,int y,int z,int l,int r)
{
    if (l==r) return b[l];
    if (a[a[y].lc].sum-a[a[x].lc].sum>=z) query(a[x].lc,a[y].lc,z,l,(l+r)>>1);
    else query(a[x].rc,a[y].rc,z-(a[a[y].lc].sum-a[a[x].lc].sum),((l+r)>>1)+1,r);
}

int main()
{
    read(T);
    while (T--)
    {
        read(n);read(m);
        memset(a,0,sizeof(a));
        for (int i=1;i<=n;i++)
            read(b[i]),d[i]=b[i];
        sort(b+1,b+1+n);
        k=unique(b+1,b+1+n)-b-1;              //离散数据
        s=0;
        build(1,n,1);
        h[0]=1;
        for (int i=1;i<=n;i++)
        {
            s++;h[i]=s;
            change(s,Hash(d[i]),1,h[i-1],1,n); 
        }
        int x,y,z;
        while(m--)
        {
            read(x);read(y);read(z);
            print(query(h[x-1],h[y],z,1,n));putchar('\n');         //区间[x,y]中的第z大数
        }
    }
    return 0;
}

树上k大

询问(u,v,k),回答u xor lastans和v这两个节点间第K小的点权

const int maxn=100010;
int n,m;
int a[maxn],b[maxn];
int pos[maxn],cnt;
struct node{int v,nxt;}E[maxn<<1];
int head[maxn],tot;
int fa[maxn],dep[maxn],top[maxn];
int size[maxn],son[maxn];
int lft[maxn<<5],rht[maxn<<5];
int rt[maxn<<5],sum[maxn<<5],sz;
int ans;

void add(int u,int v)
{
    E[++tot].nxt=head[u];
    E[tot].v=v;
    head[u]=tot;
}

void dfs1(int u,int pa)
{
    size[u]=1;
    for(int i=head[u];i;i=E[i].nxt)
    {
        int v=E[i].v;
        if(v==pa) continue;
        dep[v]=dep[u]+1;  fa[v]=u;
        dfs1(v,u);
        size[u]+=size[v];
        if(size[v]>size[son[u]]) son[u]=v;
    }
}

void dfs2(int u,int tp)
{
    top[u]=tp;
    if(son[u]) dfs2(son[u],tp);
    for(int i=head[u];i;i=E[i].nxt)
    {
        int v=E[i].v;
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}

int update(int pre,int ll,int rr,int x)
{
    int rt=++sz;
    lft[rt]=lft[pre]; rht[rt]=rht[pre]; sum[rt]=sum[pre]+1;
    if(ll<rr)
    {
        int mid=ll+rr>>1;
        if(x<=mid) lft[rt]=update(lft[pre],ll,mid,x);
        else rht[rt]=update(rht[pre],mid+1,rr,x);
    }
    return rt;
}

void dfs(int u)
{
    int x=lower_bound(pos+1,pos+1+cnt,a[u])-pos;
    rt[u]=update(rt[fa[u]],1,cnt,x);//以fa[u]为上一棵主席树建树

    for(int i=head[u];i;i=E[i].nxt)
    if(E[i].v!=fa[u]) dfs(E[i].v);
}

int LCA(int u,int v)
{
    while(top[u]!=top[v])
    {
        if(dep[top[u]]>dep[top[v]]) u=fa[top[u]];
        else v=fa[top[v]];
    }
    if(dep[u]<dep[v]) return u;
    else return v;
}

int query(int u,int v,int lca,int gra,int ll,int rr,int k)
{
    if(ll==rr) return ll;
    int x=sum[lft[u]]+sum[lft[v]]-sum[lft[lca]]-sum[lft[gra]];//差分得路径
    int mid=ll+rr>>1;
    if(x>=k) return query(lft[u],lft[v],lft[lca],lft[gra],ll,mid,k);
    else return query(rht[u],rht[v],rht[lca],rht[gra],mid+1,rr,k-x);
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
        scanf("%d",&a[i]),b[i]=a[i];
    sort(b+1,b+1+n);
    for(int i=1;i<=n;++i)
    if(i==1||b[i]!=b[i-1])
    pos[++cnt]=b[i];

    for(int i=1;i<n;++i)
    {
        int u,v;scanf("%d%d",&u,&v);
        add(u,v); add(v,u);
    }
    dfs1(1,0); dfs2(1,1);//树剖
    dfs(1);//深搜建立主席树
    while(m--)
    {
        int u,v,k;scanf("%d%d%d",&u,&v,&k);
        u^=ans;
        int lca=LCA(u,v);
        ans=pos[query(rt[u],rt[v],rt[lca],rt[fa[lca]],1,cnt,k)];//差分得到u到v的路径
        printf("%d\n",ans);
    }
    return 0;
}

带修改的k大数

1 x y:把a[x]修改为y。

2 x y k:查询[x,y]内第k小值。

const int N = 200010, M = 524289;
int n, m, cnt, i, a[N], op[N][4], b[N];
int lower(int x)
{
    int l = 1, r = cnt, t, mid;
    while (l <= r)
        if (b[mid = (l + r) >> 1] <= x)
            l = (t = mid) + 1;
        else
            r = mid - 1;
    return t;
}
struct node
{
    int val, cnt, sum, p;
    node *l, *r;
    node()
    {
        val = sum = cnt = p = 0;
        l = r = NULL;
    }
    inline void up() { sum = l->sum + r->sum + cnt; }
} *blank = new (node), *T[M], pool[2000000], *cur;
void Rotatel(node *&x)
{
    node *y = x->r;
    x->r = y->l;
    x->up();
    y->l = x;
    y->up();
    x = y;
}
void Rotater(node *&x)
{
    node *y = x->l;
    x->l = y->r;
    x->up();
    y->r = x;
    y->up();
    x = y;
}
void Ins(node *&x, int y, int p)
{
    if (x == blank)
    {
        x = cur++;
        x-> val = y;
        x-> l = x-> r = blank;
        x-> sum = x-> cnt = 1;
        x-> p = std::rand();
        return;
    }
    x-> sum += p;
    if (y == x-> val)
    {
        x-> cnt += p;
        return;
    }
    if (y<x-> val)
    {
        Ins(x-> l, y, p);
        if (x-> l-> p > x-> p)
            Rotater(x);
    }
    else
    {
        Ins(x-> r, y, p);
        if (x-> r-> p > x-> p)
            Rotatel(x);
    }
}
int Ask(node *x, int y)
{ //ask how many <= y
    int t = 0;
    while (x != blank)
        if (y<x-> val)
            x = x-> l;
        else
            t += x-> l-> sum + x-> cnt, x = x-> r;
    return t;
}
void add(int v, int i, int p)
{
    int a = 1, b = cnt, mid, f = 1, x = 1;
    while (a < b)
    {
        if (f)
            Ins(T[x], i, p);
        mid = (a + b) >> 1;
        x <<= 1;
        if (f = v <= mid)
            b = mid;
        else
            a = mid + 1, x |= 1;
    }
    Ins(T[x], i, p);
}
int kth(int l, int r, int k)
{
    int x = 1, a = 1, b = cnt, mid;
    while (a < b)
    {
        mid = (a + b) >> 1;
        x <<= 1;
        int t = Ask(T[x], r)-Ask(T[x], l-1);
        if (k <= t)
            b = mid;
        else
            k-= t, a = mid + 1, x |= 1;
    }
    return a;
}
void build(int x, int a, int b)
{
    T[x] = blank;
    if (a == b)
        return;
    int mid = (a + b) >> 1;
    build(x << 1, a, mid), build(x << 1 | 1, mid + 1, b);
}
int main()
{
    int T;read(T);
    blank-> l = blank-> r = blank;
    while (T--)
    {
        read(n);read(m);
        cur = pool;
        for (i = 1; i <= n; i++)
            read(a[i]), b[i] = a[i];
        cnt = n;
        for (i = 1; i <= m; i++)
        {
            read(op[i][0]), read(op[i][1]), read(op[i][2]);
            if (op[i][0] == 1)
                b[++cnt] = op[i][2];
            else
                read(op[i][3]);
        }
        sort(b + 1, b + cnt + 1);
        for (i = 1; i <= n; i++)
            a[i] = lower(a[i]);
        for (i = 1; i <= m; i++)
            if (op[i][0] == 1)
                op[i][2] = lower(op[i][2]);
        build(1, 1, cnt);
        for (i = 1; i <= n; i++)
            add(a[i], i, 1);
        for (i = 1; i <= m; i++)
        {
            if (op[i][0] == 1)
                add(a[op[i][1]], op[i][1],-1), add(a[op[i][1]] = op[i][2], op[i][1], 1);
            else
                printf("%d\n", b[kth(op[i][1], op[i][2], op[i][3])]);
        }
    }
}

区间不同数的个数

const int maxn = 30000 + 10;
int n,q,cnt = 0,la[1000000 + 10],a[maxn],root[maxn];
struct Node
{
    int l,r,sum;
}p[maxn*40];

int build(int l,int r)
{
    int nc = ++cnt;
    p[nc].sum = 0;
    p[nc].l = p[nc].r = 0;
    if (l == r) return nc;
    int m = l + r >> 1;
    p[nc].l = build(l,m);
    p[nc].r = build(m+1,r);
    return nc;
}
 
int update(int pos,int c,int v,int l,int r)
{
    int nc = ++cnt;
    p[nc] = p[c];
    p[nc].sum += v;
    if (l == r) return nc;
    int m = l+r>>1;
    if (m >= pos) p[nc].l = update(pos,p[c].l,v,l,m);
    else p[nc].r = update(pos,p[c].r,v,m+1,r);
    return nc;
}
 
int query(int pos,int c,int l,int r)
{
    if (l == r) return p[c].sum;
    int m = l + r >> 1;
    if (m >= pos) return p[p[c].r ].sum + query(pos,p[c].l,l,m);
    else return query(pos,p[c].r,m+1,r);
}
 
int main()
{
    scanf("%d",&n);
    memset(la,-1,sizeof la);
    for (int i = 1; i <= n; ++i)
        scanf("%d",a+i);
    root[0] = build(1,n);
    for (int i = 1 ; i <= n; ++i)
    {
        int v = a[i];
        if (la[v] == -1) root[i] = update(i,root[i-1],1,1,n);
        else{
            int t = update(la[v],root[i-1],-1,1,n);
            root[i] = update(i,t,1,1,n);
        }
        la[v] = i;
    }
    scanf("%d",&q);
    while(q--)
    {
        int x,y;
        scanf("%d %d",&x, &y);
        printf("%d\n",query(x,root[y],1,n));
    }
    return 0;
}

平衡树

fhq_treap

  1. 插入x数

  2. 删除x数(若有多个相同的数,因只删除一个)

  3. 查询x数的排名(若有多个相同的数,因输出最小的排名)

  4. 查询排名为x的数

  5. 求x的前驱(前驱定义为小于x,且最大的数)

  6. 求x的后继(后继定义为大于x,且最小的数)

const int maxn=1e5+5;
struct node
{
    int son[2],v,rnd,size;
}tr[maxn];
int n,m,l,r;
int tot,root;
int new_node(int v)//创建权值为v的结点。
{
    tot++;
    tr[tot].size=1;
    tr[tot].v=v;
    tr[tot].rnd=rand();
    tr[tot].son[0]=tr[tot].son[1]=0;
    return tot;
}
void update(int x)
{
    tr[x].size=tr[tr[x].son[0]].size+tr[tr[x].son[1]].size+1;
}
int merge(int x,int y)
{
    if(!x||!y)
        return x+y;
    if(tr[x].rnd<tr[y].rnd)
    {
        tr[x].son[1]=merge(tr[x].son[1],y);
        update(x);
        return x;
    }
    else
    {
        tr[y].son[0]=merge(x,tr[y].son[0]);
        update(y);
        return y;
    }
}
void split(int now,int k,int &x,int &y)//以权值k分离now树成x,y
{
    if(!now) x=y=0;
    else
    {
        if(tr[now].v<=k) //把所有小于等于k的权值的节点分到一棵树中
            x=now,split(tr[now].son[1],k,tr[now].son[1],y);
        else
            y=now,split(tr[now].son[0],k,x,tr[now].son[0]);
        update(now);
    }
}
void rev(int l,int r)
{
    int x,y,u,v;
    split(root,r+1,x,y);
    split(x,l,u,v);
    root=merge(merge(u,v),y);
}
void insert(int v)
{
    int x,y;
    split(root,v,x,y);
    root=merge(merge(x,new_node(v)),y);
}
void del(int v)
{
    int x,y,z;
    split(root,v,x,z);
    split(x,v-1,x,y);
    y=merge(tr[y].son[0],tr[y].son[1]);
    root=merge(merge(x,y),z);
}
void findrank(int v)
{
    int x,y;
    split(root,v-1,x,y);
    printf("%d\n",tr[x].size+1);
    root=merge(x,y);
}
int kth(int now,int k)
{
    while(1)
    {
        if(k<=tr[tr[now].son[0]].size)
            now=tr[now].son[0];
        else
        {
            if(k==tr[tr[now].son[0]].size+1) return now;
            else
            {
                k-=tr[tr[now].son[0]].size+1;
                now=tr[now].son[1];
            }
        }
    }
}
void precursor(int v)
{
    int x,y;
    split(root,v-1,x,y);
    printf("%d\n",tr[kth(x,tr[x].size)].v);
    root=merge(x,y);
}
void successor(int v)
{
    int x,y;
    split(root,v,x,y);
    printf("%d\n",tr[kth(y,1)].v);
    root=merge(x,y);
}
int main()
{
    srand((unsigned)time(NULL));
    int n,op,v;
    scanf("%d",&n);
    while(n--)
    {
        scanf("%d%d",&op,&v);
        switch(op)
        {
            case 1:insert(v);break;
            case 2:del(v);break;
            case 3:findrank(v);break;
            case 4:printf("%d\n",tr[kth(root,v)].v);break;
            case 5:precursor(v);break;
            case 6:successor(v);break;
            default:break;
        }
    }
    return 0;
}

可持久化treap

1.插入x数

2.删除x数(若有多个相同的数,因只删除一个,如果没有请忽略该操作)

3.查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)

4.查询排名为x的数

5.求x的前驱(前驱定义为小于x,且最大的数,如不存在输出-2147483647)

6.求x的后继(后继定义为大于x,且最小的数,如不存在输出2147483647)

第 i 行记为 \(v_i,opt_i,x_i\)\(v_i\)表示基于的过去版本号

#define SF scanf
#define PF printf
#define MAXN 500010
using namespace std;
struct node{
    int ch[2];
    int key,sum,fix;
    node () {}
    node (int key1):key(key1),fix(rand()),sum(1) {}
}Tree[MAXN*30];
int ncnt,root[MAXN];
void update(int x){
    Tree[x].sum=Tree[Tree[x].ch[0]].sum+Tree[Tree[x].ch[1]].sum+1;
}
int Merge(int x,int y){
    if(x==0) return y;
    if(y==0) return x;
    if(Tree[x].fix<Tree[y].fix){
        Tree[x].ch[1]=Merge(Tree[x].ch[1],y);
        update(x);
        return x;
    }
    else{
        Tree[y].ch[0]=Merge(x,Tree[y].ch[0]);
        update(y);
        return y;
    }
}
pair<int,int> Split(int x,int k){
    if(x==0)
        return make_pair(0,0);
    int newone;
    pair<int,int> y;
    if(Tree[x].key<=k){
        newone=++ncnt;
        Tree[newone]=Tree[x];
        y=Split(Tree[newone].ch[1],k);
        Tree[newone].ch[1]=y.first;
        update(newone);
        y.first=newone;
    }
    else{
        newone=++ncnt;
        Tree[newone]=Tree[x];
        y=Split(Tree[newone].ch[0],k);
        Tree[newone].ch[0]=y.second;
        update(newone);
        y.second=newone;
    }
    return y;
}
int find_kth(int now,int val){
    pair<int,int> x=Split(root[now],val-1);
    int ans=Tree[x.first].sum+1;
    root[now]=Merge(x.first,x.second);
    return ans;
}
int get_kth(int x,int k){
    if(x==0)
        return 0;
    int ch0=Tree[x].ch[0];
    if(Tree[ch0].sum>=k)
        return get_kth(ch0,k);
    if(Tree[ch0].sum+1==k)
        return Tree[x].key;
    return get_kth(Tree[x].ch[1],k-Tree[ch0].sum-1);
}
int get_pre(int now,int val){
    int k=find_kth(now,val);
    return get_kth(root[now],k-1);
}
int get_bac(int now,int val){
    pair<int,int> x=Split(root[now],val);
    int ans=get_kth(x.second,1);
    root[now]=Merge(x.first,x.second);
    return ans;
}
void Insert(int val,int now){
    pair<int,int> x=Split(root[now],val);
    Tree[++ncnt]=node(val);
    Tree[ncnt].ch[0]=0;
    Tree[ncnt].ch[1]=0;
    root[now]=Merge(Merge(x.first,ncnt),x.second);
}
void Delete(int val,int now){
    pair<int,int> x=Split(root[now],val);
    pair<int,int> y=Split(x.first,val-1);
    if(Tree[y.second].key==val)
        y.second=Merge(Tree[y.second].ch[0],Tree[y.second].ch[1]);
    root[now]=Merge(Merge(y.first,y.second),x.second);
}
int n,las,x,tag;
int main(){
    SF("%d",&n);
    Insert(-2147483647,0);
    Insert(2147483647,0);
    for(int i=1;i<=n;i++){
        SF("%d%d%d",&las,&tag,&x);
        if(Tree[root[las]].sum!=0){
            root[i]=++ncnt;
            Tree[root[i]]=Tree[root[las]];
        }
        if(tag==1)
            Insert(x,i);
        if(tag==2)
            Delete(x,i);
        if(tag==3)
            PF("%d\n",find_kth(i,x)-1);
        if(tag==4)
            PF("%d\n",get_kth(root[i],x+1));
        if(tag==5)
            PF("%d\n",get_pre(i,x));
        if(tag==6)
            PF("%d\n",get_bac(i,x));
    }
}

Segment Beats

维护一个序列,支持下面几种操作:

1.给一个区间[L, R]加上一个数x。

2.把一个区间[L, R]里小于x的数变成x。

3.把一个区间[L, R]里大于x的数变成x。

4.求区间[L, R]的和。

5.求区间[L, R]的最大值。

6.求区间[L, R]的最小值。

const int N = 1050000, inf = ~0U >> 1;
int n, m, i, a[500010], op, c, d, p, ans;
int len[N], cma[N], cmi[N], ma[N], ma2[N], mi[N], mi2[N], tma[N], tmi[N], ta[N];
long long sum[N], ret;
void tagma(int x, int p)
{
    tma[x] += p;
    if (ma[x] == mi[x])
    {
        ma[x] += p;
        mi[x] = ma[x];
        sum[x] = 1LL * ma[x] * len[x];
        return;
    }
    ma[x] += p;
    if (cma[x] + cmi[x] == len[x])
        mi2[x] += p;
    sum[x] += 1LL * p * cma[x];
}
void tagmi(int x, int p)
{
    tmi[x] += p;
    if (ma[x] == mi[x])
    {
        ma[x] += p;
        mi[x] = ma[x];
        sum[x] = 1LL * ma[x] * len[x];
        return;
    }
    mi[x] += p;
    if (cma[x] + cmi[x] == len[x])
        ma2[x] += p;
    sum[x] += 1LL * p * cmi[x];
}
void taga(int x, int p)
{
    ta[x] += p;
    ma[x] += p;
    mi[x] += p;
    if (ma2[x] !=-inf)
        ma2[x] += p;
    if (mi2[x] != inf)
        mi2[x] += p;
    sum[x] += 1LL * p * len[x];
}
void pb(int x)
{
    if (tma[x])
    {
        if (ma[x << 1] > ma[x << 1 | 1])
            tagma(x << 1, tma[x]);
        else if (ma[x << 1] < ma[x << 1 | 1])
            tagma(x << 1 | 1, tma[x]);
        else
        {
            tagma(x << 1, tma[x]);
            tagma(x << 1 | 1, tma[x]);
        }
        tma[x] = 0;
    }
    if (tmi[x])
    {
        if (mi[x << 1] < mi[x << 1 | 1])
            tagmi(x << 1, tmi[x]);
        else if (mi[x << 1] > mi[x << 1 | 1])
            tagmi(x << 1 | 1, tmi[x]);
        else
        {
            tagmi(x << 1, tmi[x]);
            tagmi(x << 1 | 1, tmi[x]);
        }
        tmi[x] = 0;
    }
    if (ta[x])
    {
        taga(x << 1, ta[x]);
        taga(x << 1 | 1, ta[x]);
        ta[x] = 0;
    }
}
void up(int x)
{
    sum[x] = sum[x << 1] + sum[x << 1 | 1];
    if (ma[x << 1] > ma[x << 1 | 1])
    {
        ma[x] = ma[x << 1];
        ma2[x] = max(ma2[x << 1], ma[x << 1 | 1]);
        cma[x] = cma[x << 1];
    }
    else if (ma[x << 1] < ma[x << 1 | 1])
    {
        ma[x] = ma[x << 1 | 1];
        ma2[x] = max(ma[x << 1], ma2[x << 1 | 1]);
        cma[x] = cma[x << 1 | 1];
    }
    else
    {
        ma[x] = ma[x << 1];
        ma2[x] = max(ma2[x << 1], ma2[x << 1 | 1]);
        cma[x] = cma[x << 1] + cma[x << 1 | 1];
    }
    if (mi[x << 1] < mi[x << 1 | 1])
    {
        mi[x] = mi[x << 1];
        mi2[x] = min(mi2[x << 1], mi[x << 1 | 1]);
        cmi[x] = cmi[x << 1];
    }
    else if (mi[x << 1] > mi[x << 1 | 1])
    {
        mi[x] = mi[x << 1 | 1];
        mi2[x] = min(mi[x << 1], mi2[x << 1 | 1]);
        cmi[x] = cmi[x << 1 | 1];
    }
    else
    {
        mi[x] = mi[x << 1];
        mi2[x] = min(mi2[x << 1], mi2[x << 1 | 1]);
        cmi[x] = cmi[x << 1] + cmi[x << 1 | 1];
    }
}
void build(int x, int a, int b)
{
    len[x] = b-a + 1;
    if (a == b)
    {
        ma[x] = mi[x] = sum[x] = ::a[a], ma2[x] =-inf, mi2[x] = inf;
        cma[x] = cmi[x] = 1;
        return;
    }
    int mid = (a + b) >> 1;
    build(x << 1, a, mid), build(x << 1 | 1, mid + 1, b);
    up(x);
}
void change(int x, int a, int b)
{
    if (c <= a && b <= d)
    {
        taga(x, p);
        return;
    }
    pb(x);
    int mid = (a + b) >> 1;
    if (c <= mid)
        change(x << 1, a, mid);
    if (d > mid)
        change(x << 1 | 1, mid + 1, b);
    up(x);
}
void cmax(int x, int a, int b)
{
    if (c <= a && b <= d)
    {
        if (mi[x] >= p)
            return;
        if (mi2[x] > p)
        {
            tagmi(x, p-mi[x]);
            return;
        }
    }
    pb(x);
    int mid = (a + b) >> 1;
    if (c <= mid)
        cmax(x << 1, a, mid);
    if (d > mid)
        cmax(x << 1 | 1, mid + 1, b);
    up(x);
}
void cmin(int x, int a, int b)
{
    if (c <= a && b <= d)
    {
        if (ma[x] <= p)
            return;
        if (ma2[x] < p)
        {
            tagma(x, p-ma[x]);
            return;
        }
    }
    pb(x);
    int mid = (a + b) >> 1;
    if (c <= mid)
        cmin(x << 1, a, mid);
    if (d > mid)
        cmin(x << 1 | 1, mid + 1, b);
    up(x);
}
void qsum(int x, int a, int b)
{
    if (c <= a && b <= d)
    {
        ret += sum[x];
        return;
    }
    pb(x);
    int mid = (a + b) >> 1;
    if (c <= mid)
        qsum(x << 1, a, mid);
    if (d > mid)
        qsum(x << 1 | 1, mid + 1, b);
}
void qmax(int x, int a, int b)
{
    if (c <= a && b <= d)
    {
        ans = max(ans, ma[x]);
        return;
    }
    pb(x);
    int mid = (a + b) >> 1;
    if (c <= mid)
        qmax(x << 1, a, mid);
    if (d > mid)
        qmax(x << 1 | 1, mid + 1, b);
}
void qmin(int x, int a, int b)
{
    if (c <= a && b <= d)
    {
        ans = min(ans, mi[x]);
        return;
    }
    pb(x);
    int mid = (a + b) >> 1;
    if (c <= mid)
        qmin(x << 1, a, mid);
    if (d > mid)
        qmin(x << 1 | 1, mid + 1, b);
}
int main()
{
    for (read(n), i = 1; i <= n; i++)
        read(a[i]);
    build(1, 1, n);
    read(m);
    while (m--)
    {
        read(op), read(c), read(d);
        if (op == 1)
            read(p), change(1, 1, n);
        if (op == 2)
            read(p), cmax(1, 1, n);
        if (op == 3)
            read(p), cmin(1, 1, n);
        if (op == 4)
            ret = 0, qsum(1, 1, n), printf("% lld\n", ret);
        if (op == 5)
            ans =-inf, qmax(1, 1, n), printf("% d\n", ans);
        if (op == 6)
            ans = inf, qmin(1, 1, n), printf("% d\n", ans);
    }
}

动态树

树的连通性

Connect u v:链接uv;

Destroy u v:断开uv;

Query u v:询问uv是否联通。

int n,m;
struct data
{
    int fa,lc,rc,rev,pfa;
}a[10010];

void Pushdown(int x)
{
    if (a[x].rev)
    {
        a[x].rev=0;
        a[a[x].lc].rev^=1;
        a[a[x].rc].rev^=1;
        swap(a[x].lc,a[x].rc);
    }
}

void Pushpath(int x)
{
    if (a[x].fa!=0) Pushpath(a[x].fa);
    Pushdown(x);
}

void zag(int x)
{
    int y=a[x].fa;
    a[x].pfa=a[y].pfa;a[y].pfa=0;
    a[y].rc=a[x].lc;
    if (a[x].lc!=0) a[a[x].lc].fa=y;
    a[x].fa=a[y].fa;
    if (a[y].fa!=0)
    {
        if (y==a[a[y].fa].lc) a[a[y].fa].lc=x;
        else a[a[y].fa].rc=x;
    }
    a[y].fa=x;a[x].lc=y;
}

void zig(int x)
{
    int y=a[x].fa;
    a[x].pfa=a[y].pfa;a[y].pfa=0;
    a[y].lc=a[x].rc;
    if (a[x].rc!=0) a[a[x].rc].fa=y;
    a[x].fa=a[y].fa;
    if (a[y].fa!=0)
    {
        if (y==a[a[y].fa].lc) a[a[y].fa].lc=x;
        else a[a[y].fa].rc=x;
    }
    a[y].fa=x;a[x].rc=y;
}

void spaly(int x)
{
    int y,z;
    Pushpath(x);
    while (a[x].fa!=0)
    {
        y=a[x].fa;
        if (a[y].fa==0)
        {
            if (x==a[y].lc) zig(x);else zag(x);
        }
        else if (a[y].lc==x)
        {
            z=a[y].fa;
            if (y==a[z].lc) zig(y),zig(x);else zig(x),zag(x);
        }
        else
        {
            z=a[y].fa;
            if (y==a[z].lc) zag(x),zig(x);else zag(y),zag(x);
        }
    }
}

void Access(int x)
{
    spaly(x);Pushdown(x);
    int y=a[x].rc;
    a[x].rc=a[y].fa=0;
    a[y].pfa=x;y=a[x].pfa;
    while (y!=0)
    {
        spaly(y);
        Pushdown(y);
        a[a[y].rc].fa=0;
        a[a[y].rc].pfa=y;
        a[y].rc=x;
        a[x].fa=y;a[x].pfa=0;
        x=y;
        y=a[x].pfa;
    }
    spaly(x);
}

void Makeroot(int x)
{
    Access(x);
    spaly(x);
    a[x].rev^=1;
    Pushdown(x);
}

int Findroot(int x)
{
    Access(x);
    spaly(x);
    Pushdown(x);
    while (a[x].lc!=0) x=a[x].lc,Pushdown(x);
    spaly(x);
    return x;
}

void Join(int x,int y)
{
    Makeroot(x);
    spaly(x);
    Pushdown(x);
    Access(y);
    spaly(y);
    Pushdown(y);
    a[x].lc=y;a[y].fa=x;
}

void Cut(int x,int y)
{
    Makeroot(x);
    Access(y);
    spaly(y);
    Pushdown(y);
    a[a[y].lc].fa=0;
    a[y].lc=0;
}

int main()
{
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    read(n);read(m);
    for (int i=1;i<=n;i++)
        a[i].fa=a[i].lc=a[i].rc=a[i].rev=a[i].pfa=0;
    int x,y;char order[20];
    while (m--)
    {
        x=read(order);
        read(x);read(y);
        if (order[0]=='Q')
        {
            if (Findroot(x)==Findroot(y)) puts("Yes");
            else puts("No");
        }
        else if (order[0]=='C') Join(x,y);
        else if (order[0]=='D') Cut(x,y);
    }
    return 0;
}

链和与最大最小

CHANGE u t : 把结点u的权值改为t

QMAX u v: 询问从点u到点v的路径上的节点的最大权值

QSUM u v: 询问从点u到点v的路径上的节点的权值和

int n,m;
struct data
{
    int fa,lc,rc,rev,pfa;
    LL xsum,lsum,rsum,lmax,rmax,xmax;
}a[300010];

void Pushdown(int x)
{
    if (a[x].rev)
    {
        a[x].rev=0;
        a[a[x].lc].rev^=1;
        a[a[x].rc].rev^=1;
        swap(a[x].lc,a[x].rc);
        swap(a[x].lsum,a[x].rsum);
        swap(a[x].lmax,a[x].rmax);
    }
}

void Pushpath(int x)
{
    if (a[x].fa!=0) Pushpath(a[x].fa);
    Pushdown(x);
}

void zag(int x)
{
    int y=a[x].fa;
    a[x].pfa=a[y].pfa;a[y].pfa=0;
    a[y].rc=a[x].lc;
    if (a[x].lc!=0) a[a[x].lc].fa=y;
    a[x].fa=a[y].fa;
    if (a[y].fa!=0)
    {
        if (y==a[a[y].fa].lc) a[a[y].fa].lc=x;
        else a[a[y].fa].rc=x;
    }
    a[y].fa=x;a[x].lc=y;
    int t=a[x].lsum;a[x].lsum+=a[y].xsum+a[y].lsum;a[y].rsum=t;
    LL q=a[x].lmax;a[x].lmax=max(max(a[x].lmax,a[y].xmax),a[y].lmax);a[y].rmax=q;
}

void zig(int x)
{
    int y=a[x].fa;
    a[x].pfa=a[y].pfa;a[y].pfa=0;
    a[y].lc=a[x].rc;
    if (a[x].rc!=0) a[a[x].rc].fa=y;
    a[x].fa=a[y].fa;
    if (a[y].fa!=0)
    {
        if (y==a[a[y].fa].lc) a[a[y].fa].lc=x;
        else a[a[y].fa].rc=x;
    }
    a[y].fa=x;a[x].rc=y;
    int t=a[x].rsum;a[x].rsum+=a[y].xsum+a[y].rsum;a[y].lsum=t;
    LL q=a[x].rmax;a[x].rmax=max(max(a[x].rmax,a[y].xmax),a[y].rmax);a[y].lmax=q;
}

void spaly(int x)
{
    int y,z;
    Pushpath(x);
    while (a[x].fa!=0)
    {
        y=a[x].fa;
        if (a[y].fa==0)
        {
            if (x==a[y].lc) zig(x);else zag(x);
        }
        else if (a[y].lc==x)
        {
            z=a[y].fa;
            if (y==a[z].lc) zig(y),zig(x);else zig(x),zag(x);
        }
        else
        {
            z=a[y].fa;
            if (y==a[z].lc) zag(x),zig(x);else zag(y),zag(x);
        }
    }
}

void Access(int x)
{
    spaly(x);Pushdown(x);
    int y=a[x].rc;
    a[x].rc=a[y].fa=0;
    a[x].rsum=0;a[x].rmax=-30010LL*200010LL;
    a[y].pfa=x;y=a[x].pfa;
    while (y!=0)
    {
        spaly(y);
        Pushdown(y);
        a[a[y].rc].fa=0;
        a[a[y].rc].pfa=y;
        a[y].rc=x;
        a[y].rsum=a[x].lsum+a[x].rsum+a[x].xsum;
        a[y].rmax=max(max(a[x].lmax,a[x].rmax),a[x].xmax);
        a[x].fa=y;a[x].pfa=0;
        x=y;
        y=a[x].pfa;
    }
    spaly(x);
}

void Makeroot(int x)
{
    Access(x);
    spaly(x);
    a[x].rev^=1;
    Pushdown(x);
}

int Findroot(int x)
{
    Access(x);
    spaly(x);
    Pushdown(x);
    while (a[x].lc!=0) x=a[x].lc,Pushdown(x);
    spaly(x);
    return x;
}

void Join(int x,int y)
{
    Makeroot(x);
    spaly(x);
    Pushdown(x);
    Access(y);
    spaly(y);
    Pushdown(y);
    a[x].lc=y;a[y].fa=x;
    a[x].lsum=a[y].xsum+a[y].lsum+a[y].rsum;
    a[x].lmax=max(max(a[y].xmax,a[y].lmax),a[y].rmax);
}

void Cut(int x,int y)
{
    Makeroot(x);
    Access(y);
    spaly(y);
    Pushdown(y);
    a[a[y].lc].fa=0;
    a[y].lc=0;
    a[y].lsum=0;a[y].lmax=-30010LL*200010LL;
}

void Updata(int x,int y)
{
    spaly(x);
    Pushdown(x);
    a[x].xmax=y;a[x].xsum=y;
}

LL querymax(int x,int y)
{
    Makeroot(x);
    Access(y);
    spaly(y);
    Pushdown(y);
    return max(a[y].lmax,a[y].xmax);
}

LL querysum(int x,int y)
{
    Makeroot(x);
    Access(y);
    spaly(y);
    Pushdown(y);
    return a[y].lsum+a[y].xsum;
}

int main()
{
freopen("bzoj_1036.in","r",stdin);
freopen("bzoj_1036.out","w",stdout);
    read(n);
    memset(a,0,sizeof(a));
    for (int i=1;i<=n;i++)
        a[i].lmax=-30010LL*200010LL,a[i].rmax=-30010LL*200010LL,a[i].xmax=-30010LL*200010LL;
    int x,y;LL z;char o[100];
    for (int i=1;i<n;i++)
        read(x),read(y),Join(x,y);
    for (int i=1;i<=n;i++)
        read(z),Updata(i,z);
    read(m);
    while (m--)
    {
        x=read(o);
        if (o[1]=='M') read(x),read(y),print(querymax(x,y)),puts("");
        else if (o[1]=='S') read(x),read(y),print(querysum(x,y)),puts("");
        else read(x),read(z),Updata(x,z);
    }
    return 0;
}

树链剖分

子树和与单链和

int n,m,point[100010],po,T;
struct data
{
    int fa,top,c,ne,d,point,l,r;
    LL x;
}a[100010];
vector<int> b[100010];
bool v[100010];
struct ST
{
    LL x,tag;
}tree[400010];

void dfs1(int x)
{
    v[x]=true;
    int Max=0,w=0;
    for (int i=0;i<b[x].size();i++)
        if (!v[b[x][i]])
        {
            a[b[x][i]].d=a[x].d+1;
            dfs1(b[x][i]);
            if (a[b[x][i]].c>Max) Max=a[b[x][i]].c,w=b[x][i];
            a[x].c+=a[b[x][i]].c;
            a[b[x][i]].fa=x;
        }
    a[x].ne=w;a[x].c++;
}

void dfs2(int x,int y)
{
    v[x]=true,point[++po]=x,a[x].point=po,a[x].top=y;a[x].l=po;
    if (!v[a[x].ne] && a[x].ne!=0) dfs2(a[x].ne,a[x].top);
    for (int i=0;i<b[x].size();i++)
            if (!v[b[x][i]])
                dfs2(b[x][i],b[x][i]);
    a[x].r=po;
}

void Pushdown(int x,int l,int r)
{
    if (tree[x].tag!=0)
    {
        tree[x<<1].tag+=tree[x].tag;
        tree[x<<1|1].tag+=tree[x].tag;
        tree[x].x+=tree[x].tag*(LL)(r-l+1);
        tree[x].tag=0;
    }
}

void change(int lq,int rq,int x,int l,int r,LL z)
{
    if (lq<=l && rq>=r) {tree[x].tag+=z;return ;}
    int mid=l+r>>1;
    Pushdown(x,l,r);
    if (lq<=mid) change(lq,rq,x<<1,l,mid,z);
    if (rq>mid) change(lq,rq,x<<1|1,mid+1,r,z);
    tree[x].x=tree[x<<1].x+tree[x<<1|1].x+tree[x<<1].tag*(LL)(mid-l+1)+tree[x<<1|1].tag*(LL)(r-mid);
}

LL query(int lq,int rq,int x,int l,int r)
{
    if (lq<=l && rq>=r) return tree[x].x+tree[x].tag*(LL)(r-l+1);
    int mid=l+r>>1;
    LL res=0;
    Pushdown(x,l,r);
    if (lq<=mid) res+=query(lq,rq,x<<1,l,mid);
    if (rq>mid) res+=query(lq,rq,x<<1|1,mid+1,r);
    tree[x].x=tree[x<<1].x+tree[x<<1|1].x+tree[x<<1].tag*(LL)(mid-l+1)+tree[x<<1|1].tag*(LL)(r-mid);
    return res; 
}

void build(int l,int r,int x)
{
    if (l==r) {tree[x].x=a[point[l]].x;return ;}
    int mid=l+r>>1;
    build(l,mid,x<<1);build(mid+1,r,x<<1|1);
    tree[x].x=tree[x<<1].x+tree[x<<1|1].x;
    tree[x].tag=0;
}

int main()
{
    read(n);read(m);
    int x,y;LL z;
    memset(a,0,sizeof(a));
    for (int i=1;i<=n;i++)
        read(a[i].x);
    for (int i=1;i<n;i++)
        read(x),read(y),b[x].push_back(y),b[y].push_back(x);
    memset(tree,0,sizeof(tree));
    memset(v,false,sizeof(v));
    a[1].d=1;a[1].fa=1;
    dfs1(1);
    memset(v,false,sizeof(v));
    x=1;int s=0;po=0;
    dfs2(1,1);
    build(1,n,1);
    while(m--)
    {
        read(x);
        if (x==1) read(x),read(z),change(a[x].point,a[x].point,1,1,n,z);
        else if (x==2) read(x),read(z),change(a[x].l,a[x].r,1,1,n,z);
        else if (x==3)
        {
            read(x);LL res=0;
            while (a[x].top!=1)
            {
                res+=query(a[a[x].top].point,a[x].point,1,1,n);
                x=a[a[x].top].fa;
            }
            res+=query(a[1].point,a[x].point,1,1,n);
            print(res);putchar('\n');
        }
    }
    return 0;
}

LCA

int n,m,point[50010],po,T,way[50010],bu[50010];
struct data
{
    int fa,top,c,ne,d,point,l,r;
    LL x;
    LL b[250];
}a[50010];
struct dara
{
    int x;LL y;
    dara(int a,LL b):x(a),y(b){};
};
vector<dara> b[50010];
bool v[50010];

void dfs1(int x)
{
    v[x]=true;
    int Max=0,w=0;
    for (int i=0;i<b[x].size();i++)
        if (!v[b[x][i].x])
        {
            a[b[x][i].x].d=a[x].d+1;
            a[b[x][i].x].x=b[x][i].y;
            dfs1(b[x][i].x);
            if (a[b[x][i].x].c>Max) Max=a[b[x][i].x].c,w=b[x][i].x;
            a[x].c+=a[b[x][i].x].c;
            a[b[x][i].x].fa=x;
        }
    a[x].ne=w;a[x].c++;
}

void dfs2(int x,int y)
{
    v[x]=true,point[++po]=x,a[x].point=po,a[x].top=y;a[x].l=po;
    if (!v[a[x].ne] && a[x].ne!=0) dfs2(a[x].ne,a[x].top);
    for (int i=0;i<b[x].size();i++)
            if (!v[b[x][i].x])
                dfs2(b[x][i].x,b[x][i].x);
    a[x].r=po;
}

void calc(int x,int y)
{
    int z=x;v[x]=true;
    for (int i=1;i<=y;i++)
    {
        z=a[z].fa;
        a[x].b[i]=a[z].b[i]+a[x].x;
    }
    for (int i=0;i<b[x].size();i++)
        if (!v[b[x][i].x]) calc(b[x][i].x,y);
}

int Find(int x,int y)    //找x向上y个
{
    while (a[x].d-a[a[x].top].d<y) y-=a[x].d-a[a[x].top].d+1,x=a[a[x].top].fa;
    return point[a[x].point-y];
}

int lca(int x,int y)     //点x、y的lca
{
    while (a[x].top!=a[y].top)
    {
        if (a[a[x].top].d<a[a[y].top].d) swap(x,y);
        x=a[a[x].top].fa;
    }
    if (a[x].d>a[y].d) swap(x,y);
    return x;
}

int dist(int x,int y)      //点x、y距离
{
    int z=lca(x,y);
    return a[x].d+a[y].d-2*a[z].d;
}

int Ans(int x,int y)
{
    LL b[50010];
    int s=0;
    while (x!=y)
    {
        if (a[x].d<a[y].d) swap(x,y);
        b[++s]=a[x].x;
        x=a[x].fa;
    }
    sort(b+1,b+1+s);
    return b[(s+1)/2];
}

int main()
{
    freopen("draw.in","r",stdin);
    freopen("draw.out","w",stdout);
    read(n);
    int x,y;LL z;
    memset(a,0,sizeof(a));
    for (int i=1;i<n;i++)
        read(x),read(y),read(z),b[x].push_back(dara(y,z)),b[y].push_back(dara(x,z));
    memset(v,false,sizeof(v));
    a[1].d=1;a[1].fa=1;
    dfs1(1);
    memset(v,false,sizeof(v));
    x=1;int s=0;po=0;
    dfs2(1,1);
    LL ans=0;
    for (int i=1;i<n;i++)
        for (int j=i+1;j<=n;j++)
                if (dist(i,j)%2==1) ans+=Ans(i,j);
    print(ans);puts("");
    return 0;
}

可并堆

Merger(i, j)。把i所在的团和j所在的团合并成一个团。如果i, j有一个人是死人,那么就忽略该命令。

Kill(i)。把i所在的团里面得分最低的人杀死。如果i这个人已经死了,这条命令就忽略。 皇帝希望他每发布一条kill命令,下面的将军就把被杀的人的分数报上来。

int n,m,fa[1000010];
bool v[1000010];
struct data
{
    int key,dist,l,r;
}tree[1000010];

int merge(int x,int y)
{
    if (x==0 || y==0) return x+y;
    if (tree[x].key>tree[y].key) swap(x,y);
    tree[x].r=merge(tree[x].r,y);
    if (tree[tree[x].r].dist>tree[tree[x].l].dist) swap(tree[x].l,tree[x].r);
    if (tree[x].r==0) tree[x].dist=0;
    else tree[x].dist=tree[tree[x].r].dist+1;
    return x;
}

int find(int x)
{
    return x==fa[x]?x:fa[x]=find(fa[x]);
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
        scanf("%d",&tree[i].key),fa[i]=i;
    scanf("%d",&m);
    char x[10];int y,z;
    memset(v,false,sizeof(v));
    while (m--)
    {
        scanf("%s",x);
        if (x[0]=='M')
        {
            scanf("%d%d",&y,&z);
            if (v[y] || v[z]) continue;
            y=find(y);z=find(z);
            if (y==z) continue;
            fa[z]=fa[y]=merge(y,z);
        }
        else
        {
            scanf("%d",&y);z=find(y);
            if (v[y]) {printf("0\n");continue;}else v[z]=true;
            printf("%d\n",tree[z].key);
            int p=merge(tree[fa[y]].l,tree[fa[y]].r);
            fa[fa[y]]=p;fa[p]=p;  
        }
    }
    return 0;
}

CDQ

三维偏序(计数)

满足\(i<j\)\(a_i<a_j\)\(b_i<b_j\)\(c_i<c_j\)的数对\((i,j)\)的个数

typedef long long ll;
const int N=5e4+10;
struct Node { int b,c,d,e; } a[N], t1[N], t2[N];
ll ans;
 
int C[N];
#define lowbit(x) x&-x
void updata(int x, int d) {
    while(x<N) {
        C[x]+=d;
        x+=lowbit(x);
    }
}
int query(int x) {
    int ret=0;
    while(x) {
        ret+=C[x];
        x-=lowbit(x);
    }
    return ret;
}
 
void merge2(int l, int r) {
    if(l==r) return;
    int mid=(l+r)>>1;
    merge2(l, mid), merge2(mid+1, r);
    int i=l, j=mid+1, k=l;
    Node *a=t1, *t=t2;
    while(i<=mid && j<=r) {
        if(a[i].c<a[j].c) {
            if(!a[i].e) updata(a[i].d, 1);
            t[k++]=a[i++];
        }
        else {
            if(a[j].e) ans+=query(a[j].d);
            t[k++]=a[j++];
        }
    }
    while(j<=r) {
        if(a[j].e) ans+=query(a[j].d);
        t[k++]=a[j++];
    }
    for(int ni=l;ni<i;ni++) if(!a[ni].e) updata(a[ni].d, -1);
    while(i<=mid) t[k++]=a[i++];
    for(int i=l;i<=r;i++) a[i]=t[i];
}
 
void merge(int l, int r) {
    if(l==r) return;
    int mid=(l+r)>>1;
    merge(l, mid), merge(mid+1, r);
    Node *t=t1;
    int i=l, j=mid+1, k=l;
    while(i<=mid && j<=r) {
        if(a[i].b<a[j].b) a[i].e=0, t[k++]=a[i++];
        else a[j].e=1, t[k++]=a[j++];
    }
    while(i<=mid) a[i].e=0, t[k++]=a[i++];
    while(j<=r) a[j].e=1, t[k++]=a[j++];
    for(int i=l;i<=r;i++) a[i]=t[i];
    merge2(l, r);
}
 
template<typename T> void gn(T &x) {
    x=0;
    char ch=getchar();
    while(ch<'0' || ch>'9') ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0', ch=getchar();
}
 
int main() {
    int n;
    gn(n);
    for(int i=1;i<=n;i++) gn(a[i].b);
    for(int i=1;i<=n;i++) gn(a[i].c);
    for(int i=1;i<=n;i++) gn(a[i].d);
    merge(1, n);
    printf("%lld\n", ans);
    return 0;
}

四维偏序(计数)

满足\(i<j\)\(a_i<a_j\)\(b_i<b_j\)\(c_i<c_j\)\(d_i<d_j\)的数对\((i,j)\)的个数

#define Inf 2e9
#define Lowbit(x) (x&-x)
const int maxn=55000;
struct Node{
    int id,x,y,z,q,flag1,flag2;
}a[maxn],b[maxn],c[maxn],d[maxn];
int ans=0,C[maxn],tim[maxn],Tim,n;
int Query(int x){
    int res=0;
    for(int i=x;i;i-=Lowbit(i)){
        if(tim[i]!=Tim){tim[i]=Tim;C[i]=0;}
        res+=C[i];
    }
    return res;
}
void Update(int x){
    for(int i=x;i<=n;i+=Lowbit(i)){
        if(tim[i]!=Tim){tim[i]=Tim;C[i]=0;}
        C[i]++;
    }
}
void CDQ3(int l,int r){
    if(l>=r)return;
    int mid=(l+r)>>1;
    CDQ3(l,mid);CDQ3(mid+1,r);
    Tim++;
    int i=l,j=mid+1,k=l;
    while(i<=mid && j<=r){
        if(c[i].z<c[j].z){
            if(c[i].flag1 && c[i].flag2)Update(c[i].q);
            d[k++]=c[i++];
        }
        else {
            if(!c[j].flag1 && !c[j].flag2)ans+=Query(c[j].q);
            d[k++]=c[j++];
        }
    }
    while(i<=mid){
        if(c[i].flag1 && c[i].flag2)Update(c[i].q);
        d[k++]=c[i++];
    }
    while(j<=r){
        if(!c[j].flag1 && !c[j].flag2)ans+=Query(c[j].q);
        d[k++]=c[j++];
    }
    for(i=l;i<=r;i++)c[i]=d[i];
}
void CDQ2(int l,int r){
    if(l>=r)return;
    int mid=(l+r)>>1;
    CDQ2(l,mid);CDQ2(mid+1,r);
    int i=l,j=mid+1,k=l;
    while(i<=mid && j<=r){
        if(b[i].y<b[j].y){
            c[k].flag2=b[i].flag2=true;c[k++]=b[i++];
        }
        else {
            c[k].flag2=b[j].flag2=false;c[k++]=b[j++];
        }
    }
    while(i<=mid){
        c[k].flag2=b[i].flag2=true;c[k++]=b[i++];
    }
    while(j<=r){
        c[k].flag2=b[j].flag2=false;c[k++]=b[j++];
    }
    for(i=l;i<=r;i++)b[i]=c[i];
    CDQ3(l,r);
}
void CDQ1(int l,int r){
    if(l>=r)return;
    int mid=(l+r)>>1;
    CDQ1(l,mid);CDQ1(mid+1,r);
    int i=l,j=mid+1,k=l;
    while(i<=mid && j<=r){
        if(a[i].x<a[j].x)b[k++]=a[i++];
        else b[k++]=a[j++];
    }
    while(i<=mid)b[k++]=a[i++];
    while(j<=r)b[k++]=a[j++];
    for(i=l;i<=r;i++){
        a[i]=b[i];
        if(a[i].id<=mid)a[i].flag1=b[i].flag1=true;
        else a[i].flag1=b[i].flag1=false;
    }
    CDQ2(l,r);
}
void Init();
int main(){
    freopen("partial_order_two.in","r",stdin);freopen("partial_order_two.out","w",stdout);
    Init();
    return 0;
}
void Init(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)a[i].id=i,scanf("%d",&a[i].x);
    for(int i=1;i<=n;i++)scanf("%d",&a[i].y);
    for(int i=1;i<=n;i++)scanf("%d",&a[i].z);
    for(int i=1;i<=n;i++)scanf("%d",&a[i].q);
    CDQ1(1,n);
    printf("%d\n",ans);
}

k维偏序(计数)

\(i<j,1<=t<=k,p^t_i<p^t_j\)的数量。

typedef bitset<40001>bit;
 
const int N=40005;
int n,k,siz,block[N];
 
struct Bitset{
    int a[N],lis[N];
    //lis[N]表示权值为n的点的编号
    bit B[201];
    //bit内储存第i维权值小于j*j的向量
    bit get(int p){
        p=a[p]; int bp=block[p]; bit ans=B[bp-1];
        for(int i=(bp-1)*siz+1;i<p;i++) ans.set(lis[i]);
        return ans;
    }
}d[7];
 
void build(int op){
    for(int i=1;i<=n;i++) d[op].lis[d[op].a[i]]=i;
    bit t; t.reset();
    for(int i=1;i<=n;i++){
        t.set(d[op].lis[i]);
        if(i%siz==0) d[op].B[i/siz]=t;
    }
}
 
int main(){
    freopen("partial_order_plus.in","r",stdin);
    freopen("partial_order_plus.out","w",stdout);
    scanf("%d%d",&n,&k); siz=int(sqrt(n*1.0));
    for(int i=1;i<=n;i++) block[i]=(i-1)/siz+1;
    for(int i=1;i<=n;i++) d[0].a[i]=i;
    for(int i=1;i<=k;i++)
     for(int j=1;j<=n;j++)
      scanf("%d",&d[i].a[j]);
    for(int i=0;i<=k;i++) build(i);
    int ans=0;
     for(int i=1;i<=n;i++){
        bit t=d[0].get(i);
        for(int j=0;j<=k;j++) t&=d[j].get(i);
        ans+=t.count();
    }
    printf("%d\n",ans);
    return 0;
}

K-D Tree

距离最近的\(m\)个点

点数量\(\le 50000\)\(m\le 10\)

const int inf=1e4+5;
int key,root,n,m,q,k,mxd;
int sqr(int x)
{
    return x*x;
}

struct point
{
    int d[7];
    friend int dis(point a,point b)
    {
        int s=0;
        for (int i=0; i<k; i++)
            s+=sqr(a.d[i]-b.d[i]);
        return s;
    }
} po;

struct node
{
    point nw;
    int son[2],mi[7],mx[7];
    friend bool operator <(node a,node b)
    {
        return a.nw.d[key]<b.nw.d[key];
    }
};

struct li
{
    point a; int l;
    friend bool operator <(li a, li b)
    {
        return a.l<b.l;
    }
} mx;
set<li> st;

struct kdtree
{
    node a[500010];
    void init()
    {

        a[0].son[0]=a[0].son[1]=0;
        for (int i=0; i<5; i++)
        {
            a[0].mx[i]=-inf;
            a[0].mi[i]=inf;
        }
    }
    void update(int x)
    {
        int l=a[x].son[0],r=a[x].son[1];
        for (int i=0; i<k; i++)
        {
            a[x].mi[i]=min(a[x].nw.d[i],min(a[l].mi[i],a[r].mi[i]));
            a[x].mx[i]=max(a[x].nw.d[i],max(a[l].mx[i],a[r].mx[i]));
        }
    }
    int build(int l,int r,int cur)
    {
        if (l>r) return 0;
        int m=(l+r)>>1;
        key=cur; nth_element(a+l,a+m,a+r+1);
        a[m].son[0]=build(l,m-1,(cur+1)%k);
        a[m].son[1]=build(m+1,r,(cur+1)%k);
        update(m);
        return m;
    }
    int getmi(int x)
    {
        int s=0;
        for (int i=0; i<k; i++)
            s+=sqr(max(po.d[i]-a[x].mx[i],0)+max(a[x].mi[i]-po.d[i],0));
        return s;
    }
    void ask(int q)
    {
        if (!q) return;
        int tmp=dis(a[q].nw,po);
        st.insert((li){a[q].nw,tmp});
        if (st.size()>m)
        {
            set<li>::iterator it=st.end(); it--;
            st.erase(it);
        }
        mxd=(*st.rbegin()).l;
        int l=a[q].son[0],r=a[q].son[1],dl=2147483647,dr=2147483647;
        if (l) dl=getmi(l);
        if (r) dr=getmi(r);
        if (dl<dr)
        {
            if (dl<mxd||st.size()<m) ask(l);
            if (dr<mxd||st.size()<m) ask(r);
        }
        else {
            if (dr<mxd||st.size()<m) ask(r);
            if (dl<mxd||st.size()<m) ask(l);
        }
    }
} kd;

int main()
{
    while (scanf("%d%d",&n,&k)!=EOF)
    {
        kd.init();
        for (int i=1; i<=n; i++)
            for (int j=0; j<k; j++)
                scanf("%d",&kd.a[i].nw.d[j]);
        root=kd.build(1,n,0);
        scanf("%d",&q);
        while (q--)
        {
            for (int i=0; i<k; i++)
                scanf("%d",&po.d[i]);
            scanf("%d",&m);
            st.clear();
            kd.ask(root);
            printf("the closest %d points are:\n",m);
            for (set<li>::iterator it=st.begin(); it!=st.end(); it++)
            {
                point ans=(*it).a;
                for (int i=0; i<k; i++)
                {
                    printf("%d",ans.d[i]);
                    if (i!=k-1) printf(" "); else puts("");
                }
            }
        }
    }
}

k远点对(二维)

给出\(N\)个点,求欧氏距离下的第\(K\)远点对

#define N 100005
#define inf 0x7fffffff
int n,m,root,Q;
struct KDtree
{
    int min[2],max[2],d[2],l,r;
    ll dis;
    bool operator < (const KDtree &a) const
    {
        return dis<a.dis;
    }
}t[N];
priority_queue <KDtree> q;

void updata(int x,int y)
{
    t[x].min[0]=min(t[x].min[0],t[y].min[0]);
    t[x].max[0]=max(t[x].max[0],t[y].max[0]);
    t[x].min[1]=min(t[x].min[1],t[y].min[1]);
    t[x].max[1]=max(t[x].max[1],t[y].max[1]);
}

int build(int l,int r,int now)
{
    for (int i=l;i<=r;i++)
        t[i].dis=t[i].d[now];
    int mid=(l+r)/2;
    nth_element(t+l,t+mid,t+r+1);
    t[mid].min[0]=t[mid].max[0]=t[mid].d[0];
    t[mid].min[1]=t[mid].max[1]=t[mid].d[1];
    if (l<mid) t[mid].l=build(l,mid-1,1-now);
    if (r>mid) t[mid].r=build(mid+1,r,1-now);
    if (t[mid].l) updata(mid,t[mid].l);
    if (t[mid].r) updata(mid,t[mid].r);
    return mid;
}

ll dist(int x,int y)
{
    return (ll)(t[x].d[0]-t[y].d[0])*(t[x].d[0]-t[y].d[0])+(ll)(t[x].d[1]-t[y].d[1])*(t[x].d[1]-t[y].d[1]);
}

ll get(int x)
{
    ll ret=0;
    ret+=max((ll)(t[Q].d[0]-t[x].min[0])*(t[Q].d[0]-t[x].min[0]),(ll)(t[x].max[0]-t[Q].d[0])*(t[x].max[0]-t[Q].d[0]));
    ret+=max((ll)(t[Q].d[1]-t[x].min[1])*(t[Q].d[1]-t[x].min[1]),(ll)(t[x].max[1]-t[Q].d[1])*(t[x].max[1]-t[Q].d[1]));
    return ret;
}

void solve(int x)
{
    t[x].dis=-dist(x,Q);
    if (q.size()<m) q.push(t[x]);
    else
    {
        KDtree u=q.top();
        if (u.dis>t[x].dis)
        {
            q.pop();
            q.push(t[x]);
        }
    }
    ll dl=inf,dr=inf;
    if (t[x].l) dl=-get(t[x].l);
    if (t[x].r) dr=-get(t[x].r);
    if (dl<dr)
    {
        if (dl<q.top().dis||q.size()<m&&t[x].l) solve(t[x].l);
        if (dr<q.top().dis||q.size()<m&&t[x].r) solve(t[x].r);
    }else
    {
        if (dr<q.top().dis||q.size()<m&&t[x].r) solve(t[x].r);
        if (dl<q.top().dis||q.size()<m&&t[x].l) solve(t[x].l);
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    m*=2;
    for (int i=1;i<=n;i++)
        scanf("%d%d",&t[i].d[0],&t[i].d[1]);
    root=build(1,n,0);
    for (int i=1;i<=n;i++)
    {
        Q=i;
        solve(root);
    }
    printf("%lld",-q.top().dis);
    return 0;
}

维护二维区间和

1 x y A 在坐标\((x,y)\)增加权值A

2 x1 y1 x2 y2 输出x1 y1 x2 y2这个矩形内的数字和

3 终止程序运行

inline void read(int &x){
    x=0;char ch;bool flag = false;
    while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
inline int cat_max(const int &a,const int &b){return a>b ? a:b;}
inline int cat_min(const int &a,const int &b){return a<b ? a:b;}
const int maxn = 160010;
const int lim_siz = 2000;
int split[maxn],now;
int dem = 2;
struct Node{
    int pos[2],val;
    int minn[2],maxx[2],sum;
    Node *ch[2];
    void update(){
        for(int d=0;d<dem;++d) minn[d] = min(pos[d],min(ch[0]->minn[d],ch[1]->minn[d]));
        for(int d=0;d<dem;++d) maxx[d] = max(pos[d],max(ch[0]->maxx[d],ch[1]->maxx[d]));
        sum = val + ch[0]->sum + ch[1]->sum;
    }
  
}*null,*root,*op;
Node T[maxn];
inline bool cmp(const Node &a,const Node &b){
    return a.pos[split[now]] < b.pos[split[now]];
}
inline void init(){
    null = &T[0];
    null->ch[0] = null->ch[1] = null;
    null->val = 0;
    for(int d=0;d<dem;++d){
        null->pos[d] = 0;
        null->minn[d] = 0x3f3f3f3f;
        null->maxx[d] = -0x3f3f3f3f;
    }root = null;
}
Node* build(int l,int r,int s){
    if(l > r) return null;
    int mid = (l+r)>> 1;
    split[now = mid] = s % dem;
    nth_element(T+l,T+mid,T+r+1,cmp);
    Node *p = &T[mid];
    p->ch[0] = build(l,mid-1,s+1);
    p->ch[1] = build(mid+1,r,s+1);
    p->update();return p;
}
int sav[maxn][2],sav_cnt,cnt;
int val[maxn],cmd,X1,Y1,X2,Y2,x;
int query(Node *p){
    if(p == null) return 0;
    if(p->minn[0] >= X1 && p->minn[1] >= Y1
    && p->maxx[0] <= X2 && p->maxx[1] <= Y2)
        return p->sum;
    else if(p->maxx[1] < Y1 || p->maxx[0] < X1 
         || p->minn[0] > X2 || p->minn[1] > Y2)
        return 0;
    if( p->pos[0] >= X1 && p->pos[1] >= Y1
    &&  p->pos[0] <= X2 && p->pos[1] <= Y2)
        return p->val + query(p->ch[0]) + query(p->ch[1]);
    return query(p->ch[0]) + query(p->ch[1]);
}
int main(){
    init();
    int n;read(n);//read(n);
    int lastans = 0;
    while(1){
        read(cmd);
        if(cmd == 3) break;
        if(cmd == 1){
            read(X1);read(Y1);read(x);
            X1 ^= lastans;Y1 ^= lastans;x ^= lastans;
            sav[++sav_cnt][0] = X1;
            sav[sav_cnt][1] = Y1;
            val[sav_cnt] = x;
            if(sav_cnt == lim_siz){
                for(int i=1;i<=sav_cnt;++i){
                    T[cnt+i].minn[0] = T[cnt+i].maxx[0] = T[cnt+i].pos[0] = sav[i][0];
                    T[cnt+i].minn[1] = T[cnt+i].maxx[1] = T[cnt+i].pos[1] = sav[i][1];
                    T[cnt+i].val = val[i];
                }root = build(1,cnt+=sav_cnt,1);
                sav_cnt = 0;
            }
        }else{
            read(X1);read(Y1);read(X2);read(Y2);
            X1 ^= lastans;Y1 ^= lastans;
            X2 ^= lastans;Y2 ^= lastans;
            int ans = 0;
            for(int i=1;i<=sav_cnt;++i){
                if(sav[i][0] >= X1 && sav[i][1] >= Y1
                && sav[i][0] <= X2 && sav[i][1] <= Y2)
                    ans += val[i];
            }
            ans += query(root);
            printf("%d\n",lastans = ans);
        }
    }
    return 0;
}

统计某一区间的最大值(三维)

\([l,r]\)之间找一个在这个区间里只出现过一次且最大的数

对于每一个数维护三个维度:(自己的位置\(x\),相同数前一个位置\(y\),相同数后一个位置\(z\)

我们要的是\(x>=l\)\(x<=r\)\(y<l\)且$ z>r$的最大数。

#define N 200003
int n,m,cmpd,root,pre[N],next[N],point[N],ans,lastans,x,y,val[N];
struct data {
    int d[3],mx[3],mn[3],val,maxn;
    int l,r;
}tr[N];
int tmp[10][10];
int cmp(data a,data b)
{
    return a.d[cmpd]<b.d[cmpd];
}
void update(int now)
{
    int l=tr[now].l; int r=tr[now].r;
    for (int i=0;i<=2;i++){
        if (l) tr[now].mx[i]=max(tr[now].mx[i],tr[l].mx[i]),
               tr[now].mn[i]=min(tr[now].mn[i],tr[l].mn[i]);
        if (r) tr[now].mx[i]=max(tr[now].mx[i],tr[r].mx[i]),
               tr[now].mn[i]=min(tr[now].mn[i],tr[r].mn[i]);
    }
    if (l) tr[now].maxn=max(tr[l].maxn,tr[now].maxn);
    if (r) tr[now].maxn=max(tr[r].maxn,tr[now].maxn);
}
int build(int l,int r,int d)
{
    d%=3;
    cmpd=d;
    int mid=(l+r)/2;
    nth_element(tr+l,tr+mid,tr+r+1,cmp);
    for (int i=0;i<=2;i++)
     tr[mid].mx[i]=tr[mid].mn[i]=tr[mid].d[i];
    //cout<<tr[mid].d[0]<<" "<<tr[mid].d[1]<<" "<<tr[mid].d[2]<<endl;;
    if (l<mid) tr[mid].l=build(l,mid-1,d+1);
    if (r>mid) tr[mid].r=build(mid+1,r,d+1);
    update(mid);
    return mid;
}
int check(int now)
{
    if (tr[now].mx[0]<x||tr[now].mn[0]>y) return 0;
    if (tr[now].mn[1]>=x) return 0;
    if (tr[now].mx[2]<=y) return 0;
    return 1;  
}
void query(int now)
{
    if (tr[now].d[0]>=x&&tr[now].d[0]<=y&&tr[now].d[1]<x&&tr[now].d[2]>y)
     ans=max(ans,tr[now].val);
    int l=tr[now].l; int r=tr[now].r;
    if (l&&tr[l].maxn>ans) 
     if (check(l)) query(l);
    if (r&&tr[r].maxn>ans)
     if (check(r)) query(r);
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&val[i]);
    memset(point,0,sizeof(point));
    for (int i=1;i<=n;i++)
     pre[i]=point[val[i]],point[val[i]]=i;
    for (int i=1;i<=n;i++) point[i]=n+1;
    for (int i=n;i>=1;i--) 
     next[i]=point[val[i]],point[val[i]]=i;
    for (int i=1;i<=n;i++)
     tr[i].d[0]=i,tr[i].d[1]=pre[i],tr[i].d[2]=next[i],tr[i].val=tr[i].maxn=val[i];
    root=build(1,n,0);
    lastans=0;
    for (int i=1;i<=m;i++) {
        scanf("%d%d",&x,&y);
        x=(x+lastans)%n+1;
        y=(y+lastans)%n+1;
        if (x>y) swap(x,y);
        ans=0;
        query(root);
        printf("%d\n",ans);
        lastans=ans;
    }
}

倍增思想

一维倍增

int n,a[300010],f[300010][30],ans[300010];

int GCD(int x,int y)
{
    int r=x%y;
    while (r!=0) x=y,y=r,r=x%y;
    return y;
}

void init()
{
    for (int i=1;i<=n;i++)
        f[i][0]=a[i];
    for (int i=1;(1<<i)<=n;i++)
        for (int j=1;j+(1<<i)-1<=n;j++)
            f[j][i]=GCD(f[j][i-1],f[j+(1<<(i-1))][i-1]);
}

int query(int l,int r)
{
    int k=(int)(log(double(r-l+1))/log((double)2));
    return GCD(f[l][k],f[r-(1<<k)+1][k]);
}

int main()
{ 
    cin>>n;
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    init();
    return 0;
}

二维倍增

const int maxn=1010,lgn =11,INF =1e9;
int mymax(int a,int b,int c,int d){return max(max(max(a,b),c),d);}
int mymin(int a,int b,int c,int d){return min(min(min(a,b),c),d);}
int mx[maxn][maxn][lgn],mn[maxn][maxn][lgn];
int Log[maxn],A,B,n;
void st_pre()
{
    for(int k=1;k<=Log[min(A,B)];k++)
        for(int i=1;i+(1<<k)-1<=A;i++)
            for(int j=1;j+(1<<k)-1<=B;j++)
            {
                mx[i][j][k]=mymax(mx[i][j][k-1],mx[i+(1<<k-1)][j][k-1],mx[i][j+(1<<k-1)][k-1],mx[i+(1<<k-1)][j+(1<<k-1)][k-1]);
                mn[i][j][k]=mymin(mn[i][j][k-1],mn[i+(1<<k-1)][j][k-1],mn[i][j+(1<<k-1)][k-1],mn[i+(1<<k-1)][j+(1<<k-1)][k-1]);
            }   
}
int st_max(int x1,int y1,int x2,int y2)
{
    int k=Log[n];
    return mymax(mx[x1][y1][k],mx[x2-(1<<k)+1][y1][k],mx[x1][y2-(1<<k)+1][k],mx[x2-(1<<k)+1][y2-(1<<k)+1][k]);
}
int st_min(int x1,int y1,int x2,int y2)
{
    int k=Log[n];
    return mymin(mn[x1][y1][k],mn[x2-(1<<k)+1][y1][k],mn[x1][y2-(1<<k)+1][k],mn[x2-(1<<k)+1][y2-(1<<k)+1][k]);
}
int main()
{
    scanf("%d%d%d",&A,&B,&n);
    for(int i=1;i<=A;i++)
        for(int j=1;j<=B;j++)
        {
            int x; scanf("%d",&x);
            mx[i][j][0]=mn[i][j][0]=x;
        }
    Log[1]=0; for(int i=2;i<=max(A,B);i++) Log[i]=Log[i/2]+1;
    st_pre();
    int ans=INF;
    for(int i=1;i+n-1<=A;i++)
        for(int j=1;j+n-1<=B;j++)
            ans=min(ans,st_max(i,j,i+n-1,j+n-1)-st_min(i,j,i+n-1,j+n-1));
    printf("%d\n",ans);
    return 0;
}

树上倍增

int n,m,S,c[100010],h[100010],to[200010],ne[200010],d[100010],p[200010][20];

void dfs(int u)
{
    c[u]=1;
    for (int i=h[u];i!=-1;i=ne[i])
    {
        if (!d[to[i]])
        {
            d[to[i]]=d[u]+1;
            p[to[i]][0]=u;
            dfs(to[i]);
            c[u]+=c[to[i]];
        }
    }
}

void init()
{
    for (int j=1;(1<<j)<=n;j++)
        for (int i=1;i<=n;i++)
            if (p[i][j-1]!=-1) p[i][j]=p[p[i][j-1]][j-1];
}

int lca(int x,int y)
{
    if (d[x]<d[y]) swap(x,y);
    int i;
    for (i=0;(1<<i)<=d[x];i++);i--;
    for (int j=i;j>=0;j--)
        if (d[x]-(1<<j)>=d[y]) x=p[x][j];
    if (x==y) return x;
    for (int j=i;j>=0;j--)
        if (p[x][j]!=-1 && p[x][j]!=p[y][j])
            x=p[x][j],y=p[y][j];
    return p[x][0];
}

void Add(int x,int y)
{
    S++;to[S]=x;ne[S]=h[y];h[y]=S;
    S++;to[S]=y;ne[S]=h[x];h[x]=S;
}

int Find(int x,int y)
{
    y=d[x]-y;
    int i;
    for (i=0;(1<<i)<=d[x];i++);i--;
    for (int j=i;j>=0;j--)
        if (d[x]-(1<<j)>=y) x=p[x][j];
    return x;
}

int main()
{
    cin>>n;m=n-1;
    memset(h,-1,sizeof(h));
    memset(d,0,sizeof(d));
    S=0;
    int x,y,q;
    while(m--) scanf("%d%d",&x,&y),Add(x,y);
    d[1]=1;dfs(1);init();
    return 0;
}

Top Tree

const int N = 100010 * 2, inf = ~0U >> 1;
struct tag
{
    int a, b; //ax+b
    tag() { a = 1, b = 0; }
    tag(int x, int y) { a = x, b = y; }
    bool ex() { return a != 1 || b; }
    tag operator+(const tag &x) { return tag(a * x.a, b * x.a + x.b); }
};
int atag(int x, tag y) { return x * y.a + y.b; }
struct data
{
    int sum, minv, maxv, size;
    data() { sum = size = 0, minv = inf, maxv =-inf; }
    data(int x) { sum = minv = maxv = x, size = 1; }
    data(int a, int b, int c, int d) { sum = a, minv = b, maxv = c, size = d; }
    data operator+(const data &x)
    {
        return data(sum + x.sum, min(minv, x.minv), max(maxv, x.maxv), size + x.size);
    }
};
data operator+(const data &a, const tag &b)
{
    return a.size ? data(a.sum * b.a + a.size * b.b, atag(a.minv, b), atag(a.maxv, b), a.size) : a;
}

int f[N], son[N][4], a[N], tot, rt, rub, ru[N];
bool rev[N], in[N];
int val[N];
data csum[N], tsum[N], asum[N];
tag ctag[N], ttag[N];
bool isroot(int x, int t)
{
    if (t)
        return !f[x] || !in[f[x]] || !in[x];
    return !f[x] || (son[f[x]][0] != x && son[f[x]][1] != x) || in[f[x]] || in[x];
}
void rev1(int x)
{
    if (!x)
        return;
    swap(son[x][0], son[x][1]);
    rev[x] ^= 1;
}
void tagchain(int x, tag p)
{
    if (!x)
        return;
    csum[x] = csum[x] + p;
    asum[x] = csum[x] + tsum[x];
    val[x] = atag(val[x], p);
    ctag[x] = ctag[x] + p;
}
void tagtree(int x, tag p, bool t)
{
    if (!x)
        return;
    tsum[x] = tsum[x] + p;
    ttag[x] = ttag[x] + p;
    if (!in[x] && t)
        tagchain(x, p);
    else
        asum[x] = csum[x] + tsum[x];
}
void pb(int x)
{
    if (!x)
        return;
    if (rev[x])
        rev1(son[x][0]), rev1(son[x][1]), rev[x] = 0;
    if (!in[x] && ctag[x].ex())
    {
        tagchain(son[x][0], ctag[x]);
        tagchain(son[x][1], ctag[x]);
        ctag[x] = tag();
    }
    if (ttag[x].ex())
    {
        tagtree(son[x][0], ttag[x], 0), tagtree(son[x][1], ttag[x], 0);
        tagtree(son[x][2], ttag[x], 1), tagtree(son[x][3], ttag[x], 1);
        ttag[x] = tag();
    }
}
void up(int x)
{
    tsum[x] = data();
    for (int i = 0; i < 2; i++)
        if (son[x][i])
            tsum[x] = tsum[x] + tsum[son[x][i]];
    for (int i = 2; i < 4; i++)
        if (son[x][i])
            tsum[x] = tsum[x] + asum[son[x][i]];
    if (in[x])
    {
        csum[x] = data();
        asum[x] = tsum[x];
    }
    else
    {
        csum[x] = data(val[x]);
        for (int i = 0; i < 2; i++)
            if (son[x][i])
                csum[x] = csum[x] + csum[son[x][i]];
        asum[x] = csum[x] + tsum[x];
    }
}
int child(int x, int t)
{
    pb(son[x][t]);
    return son[x][t];
}
void rotate(int x, int t)
{
    int y = f[x], w = (son[y][t + 1] == x) + t;
    son[y][w] = son[x][w ^ 1];
    if (son[x][w ^ 1])
        f[son[x][w ^ 1]] = y;
    if (f[y])
        for (int z = f[y], i = 0; i < 4; i++)
            if (son[z][i] == y)
                son[z][i] = x;
    f[x] = f[y];
    f[y] = x;
    son[x][w ^ 1] = y;
    up(y);
}
void splay(int x, int t = 0)
{
    int s = 1, i = x, y;
    a[1] = i;
    while (!isroot(i, t))
        a[++s] = i = f[i];
    while (s)
        pb(a[s--]);
    while (!isroot(x, t))
    {
        y = f[x];
        if (!isroot(y, t))
        {
            if ((son[f[y]][t] == y) ^ (son[y][t] == x))
                rotate(x, t);
            else
                rotate(y, t);
        }
        rotate(x, t);
    }
    up(x);
}
int newnode()
{
    int x = rub ? ru[rub--] : ++tot;
    son[x][2] = son[x][3] = 0;
    in[x] = 1;
    return x;
}
void setson(int x, int t, int y)
{
    son[x][t] = y;
    f[y] = x;
}
int pos(int x)
{
    for (int i = 0; i < 4; i++)
        if (son[f[x]][i] == x)
            return i;
    return 4;
}
void add(int x, int y)
{
    if (!y)
        return;
    pb(x);
    for (int i = 2; i < 4; i++)
        if (!son[x][i])
        {
            setson(x, i, y);
            return;
        }
    while (son[x][2] && in[son[x][2]])
        x = child(x, 2);
    int z = newnode();
    setson(z, 2, son[x][2]);
    setson(z, 3, y);
    setson(x, 2, z);
    splay(z, 2);
}
void del(int x)
{
    if (!x)
        return;
    splay(x);
    if (!f[x])
        return;
    int y = f[x];
    if (in[y])
    {
        int s = 1, i = y, z = f[y];
        a[1] = i;
        while (!isroot(i, 2))
            a[++s] = i = f[i];
        while (s)
            pb(a[s--]);
        if (z)
        {
            setson(z, pos(y), child(y, pos(x) ^ 1));
            splay(z, 2);
        }
        ru[++rub] = y;
    }
    else
    {
        son[y][pos(x)] = 0;
        splay(y);
    }
    f[x] = 0;
}
int fa(int x)
{
    splay(x);
    if (!f[x])
        return 0;
    if (!in[f[x]])
        return f[x];
    int t = f[x];
    splay(t, 2);
    return f[t];
}
int access(int x)
{
    int y = 0;
    for (; x; y = x, x = fa(x))
    {
        splay(x);
        del(y);
        add(x, son[x][1]);
        setson(x, 1, y);
        up(x);
    }
    return y;
}
int lca(int x, int y)
{
    access(x);
    return access(y);
}
int root(int x)
{
    access(x);
    splay(x);
    while (son[x][0])
        x = son[x][0];
    return x;
}
void makeroot(int x)
{
    access(x);
    splay(x);
    rev1(x);
}
void link(int x, int y)
{
    makeroot(x);
    add(y, x);
    access(x);
}
void cut(int x)
{
    access(x);
    splay(x);
    f[son[x][0]] = 0;
    son[x][0] = 0;
    up(x);
}
void changechain(int x, int y, tag p)
{
    makeroot(x);
    access(y);
    splay(y);
    tagchain(y, p);
}
data askchain(int x, int y)
{
    makeroot(x);
    access(y);
    splay(y);
    return csum[y];
}
void changetree(int x, tag p)
{
    access(x);
    splay(x);
    val[x] = atag(val[x], p);
    for (int i = 2; i < 4; i++)
        if (son[x][i])
            tagtree(son[x][i], p, 1);
    up(x);
    splay(x);
}
data asktree(int x)
{
    access(x);
    splay(x);
    data t = data(val[x]);
    for (int i = 2; i < 4; i++)
        if (son[x][i])
            t = t + asum[son[x][i]];
    return t;
}
int n, m, x, y, z, k, i, ed[N][2];
int main()
{
    read(n);  //n个点 
    read(m);  //m个询问 
    tot = n;
    for (i = 1; i < n; i++)   //边 
        read(ed[i][0]), read(ed[i][1]);
    for (i = 1; i <= n; i++)  //点权 
        read(val[i]), up(i);
    for (i = 1; i < n; i++)
        link(ed[i][0], ed[i][1]);
    read(rt);
    makeroot(rt);
    while (m--)
    {
    read(k);
    if (k == 1)
    {//换根
        read(rt);
        makeroot(rt);
    }
    if (k == 9)
    { //x的父亲变成y
        read(x), read(y);
        if (lca(x, y) == x) continue;
        cut(x);
        link(y, x);
        makeroot(rt);
    }
    if (k == 0)
    { //子树赋值
        read(x), read(y);
        changetree(x, tag(0, y));
    }
    if (k == 5)
    { //子树加
        read(x), read(y);
        changetree(x, tag(1, y));
    }
    if (k == 3)
    { //子树最小值
        read(x);
        printf("%d\n", asktree(x).minv);
    }
    if (k == 4)
    { //子树最大值
        read(x);
        printf("%d\n", asktree(x).maxv);
    }
    if (k == 11)
    { //子树和
        read(x);
        printf("%d\n", asktree(x).sum);
    }
    if (k == 2)
    { //链赋值
        read(x), read(y), read(z);
        changechain(x, y, tag(0, z));
        makeroot(rt);
    }
    if (k == 6)
    { //链加
        read(x), read(y), read(z);
        changechain(x, y, tag(1, z));
        makeroot(rt);
    }
    if (k == 7)
    { //链最小值
        read(x), read(y);
        printf("%d\n", askchain(x, y).minv);
        makeroot(rt);
    }
    if (k == 8)
    { //链最大值
        read(x), read(y);
        printf("%d\n", askchain(x, y).maxv);
        makeroot(rt);
    }
    if (k == 10)
    { //链和
        read(x), read(y);
        printf("%d\n", askchain(x, y).sum);
        makeroot(rt);
    }
    }
}

字符串处理

AC自动机

#include<cstring>
#include<queue>
#include<cstdio>
#include<map>
#include<string>
using namespace std;
const int SIGMA_SIZE = 26;
const int MAXNODE = 11000;
const int MAXS = 150 + 10;

struct AhoCorasickAutomata {
  int ch[MAXNODE][SIGMA_SIZE];
  int f[MAXNODE];    // fail函数
  int val[MAXNODE];  // 每个字符串的结尾结点都有一个非0的val
  int last[MAXNODE]; // 输出链表的下一个结点
  int match[MAXNODE]; // 表示这个点是结点
  int cnt[MAXS]; //用来统计模式串被找到了几次
  int sz;

  void init() {
    sz = 1;
    memset(ch[0], 0, sizeof(ch[0]));
    memset(cnt, 0, sizeof(cnt));
    memset(match, 0, sizeof(match));
  }

  // 字符c的编号
  int idx(char c) {
    return c-'a';
  }

  // 插入字符串。v必须非0
  void insert(char *s, int v) {
    int u = 0, n = strlen(s);
    for(int i = 0; i < n; i++) {
      int c = idx(s[i]);
      if(!ch[u][c]) {
        memset(ch[sz], 0, sizeof(ch[sz]));
        val[sz] = 0;
        ch[u][c] = sz++;
      }
      u = ch[u][c];
    }
    val[u] = v;
  }

  // 递归打印以结点j结尾的所有字符串
  void print(int j) {
    if(j) {
      cnt[val[j]]++;
      //match[j] = 1;
      print(last[j]);
    }
  }

  // 在T中找模板
  int find(char* T) {
    int n = strlen(T);
    int j = 0; // 当前结点编号,初始为根结点
    for(int i = 0; i < n; i++) { // 文本串当前指针
      int c = idx(T[i]);
      j = ch[j][c];
      if(val[j]) print(j);
      else if(last[j]) print(last[j]); // 找到了!
    }
  }

  // 计算fail函数
  void getFail() {
    queue<int> q;
    f[0] = 0;
    // 初始化队列
    for(int c = 0; c < SIGMA_SIZE; c++) {
      int u = ch[0][c];
      if(u) { f[u] = 0; q.push(u); last[u] = 0; }
    }
    // 按BFS顺序计算fail
    while(!q.empty()) {
      int r = q.front(); q.pop();
      for(int c = 0; c < SIGMA_SIZE; c++) {
        int u = ch[r][c];
        if(!u) {ch[r][c] = ch[f[r]][c];continue;}
        q.push(u);
        int v = f[r];
        while(v && !ch[v][c]) v = f[v];
        f[u] = ch[v][c];
        last[u] = val[f[u]] ? f[u] : last[f[u]];
      }
    }
    /* *when Matrix need
    for(int i = 0; i < sz; i++) {
        if(val[i]) print(i);
        else if(last[i]) print(i);
    }
    */
    /* 统计长度为n的串有多种可能不出现模板串,需要Matrix
    int doit(int n) {
        matrix A(sz, sz);
        for(int i = 0; i < sz; i++) {
            if(match[i]) continue;
            for(int c = 0; c < SIGMA_SIZE; c++) {
                if(!match[ch[i][c]]) A[i][ch[i][c]]++;
            }
        }
        A = A ^ n;
        int ans = 0;
        for(int i = 0; i < sz; i++) {
            ans += A[0][i];
            ans %= MOD;
        }
        return ans;
    }
    */
  }

};

AhoCorasickAutomata ac;
char text[1000001], P[151][80];
int n, T;
map<string,int> ms;

int main()
{
  while(scanf("%d", &n) == 1 && n)
  {
    ac.init();
    for(int i = 1; i <= n; i++)
    {
      scanf("%s", P[i]);
      ac.insert(P[i], i);
    }
    ac.getFail();
    scanf("%s", text);
    ac.find(text);
    int best =  -1;
    for(int i = 1; i <= n; i++)
      if(ac.cnt[i] > best) best = ac.cnt[i];
    printf("%d\n", best);
    for(int i = 1; i <= n; i++)
      if(ac.cnt[ms[string(P[i])]] == best) printf("%s\n", P[i]);
  }
  return 0;
}

KMP

char a1[2000000],a2[2000000];
int kmp[2000000];
int main()
{
    scanf("%s%s",a1,a2);
    kmp[0]=kmp[1]=0;//前一位,两位失配了,都只可能将第一位作为新的开头
    int len1=strlen(a1),len2=strlen(a2);
    int k;
    k=0;
    for(int i=1;i<len2;i++)//自己匹配自己
    {
        while(k&&a2[i]!=a2[k])k=kmp[k];//找到最长的前后缀重叠长度
        kmp[i+1]=a2[i]==a2[k]?++k:0;//不相等的情况,即无前缀能与后缀重叠,直接赋值位0(注意是给下一位,因为匹配的是下一位适失配的情况)
    }
    k=0;
    for(int i=0;i<len1;i++)
    {
        while(k&&a1[i]!=a2[k])k=kmp[k];//如果不匹配,则将利用kmp数组往回跳
        k+=a1[i]==a2[k]?1:0;//如果相等了,则匹配下一位
        if(k==len2)printf("%d\n",i-len2+2);//如果已经全部匹配完毕,则输出初始位置
    }
    for(int i=1;i<=len2;i++)printf("%d ",kmp[i]);//输出f数组
    return 0;
}

拓展KMP

求s1的前缀和s2的后缀最大匹配。

#define maxn 111111
char a[maxn],b[maxn];
int nex[maxn];
int main()
{
    while(scanf("%s%s",a,b)!=EOF)
    {
        int len1=strlen(a);
        int len2=strlen(b);
        int len=min(len1,len2);//最大匹配长度必须不大于两串串长较小值 
        for(int i=len1,j=0;i<len1+len2;i++,j++)//将a串b串合并 
            a[i]=b[j];
        int la=len1+len2;//总串长 
        for(int i=0,j=-1;i<=la;i++,j++)//求next数组 
        {
            nex[i]=j;
            while(~j&&a[i]!=a[j])
                j=nex[j];
        }
        while(nex[la]>len)
            la=nex[la];
        if(nex[la]==0)//最大匹配长度为0直接输出0 
            printf("0\n");
        else
        {
            for(int i=0;i<nex[la];i++)//输出最大匹配 
                printf("%c",a[i]);
            printf(" %d\n",nex[la]);//输出最大匹配长度 
        }
    }
    return 0;
}

后缀数组

第一行\(n\)个整数,第\(i\)个整数表示排名为\(i\)的后缀的第一个字符在原串中的位置。

第二行\(n−1\)个整数,第\(i\)个整数表示排名为\(i\)和排名为\(i+1\)的后缀的最长公共前缀的长度。

时间复杂度\(O(n)\)

int n,m,s,b[200010],c[200010],d[200010];
char sx[200010],sy[200010];
struct data
{
    int len,fa,letter[26],tree[26],id,flag;
}a[200010];
int sa[200010],rank[200010],RANK;

void Extend(int x,int p)
{
    s++;int q=s;a[q].len=a[p].len+1;
    while (p!=0 && a[p].letter[x]==0)
        a[p].letter[x]=q,p=a[p].fa;
    if (p==0) {a[q].fa=1;return ;}
    int np=a[p].letter[x];
    if (a[np].len==a[p].len+1) a[q].fa=np;
    else
    {
        s++;int nq=s;a[nq].len=a[p].len+1;
        for (int i=0;i<26;i++)
            a[nq].letter[i]=a[np].letter[i];
        a[nq].id=a[np].id;
        a[nq].fa=a[np].fa;a[np].fa=nq;a[q].fa=nq;
        while (p!=0&&a[p].letter[x]==np)
            a[p].letter[x]=nq,p=a[p].fa;
    }
}

void Insert(char x[])
{
    int y=strlen(x);
    s=1;int z=1;
    for (int i=y-1;i>=0;i--)
        Extend(x[i]-'a',z),z=a[z].letter[x[i]-'a'],a[z].id=i+1,d[i+1]=z,a[z].flag=1;
}

void dfs(int x)
{
    if(a[x].id!=0 && a[x].flag) sa[++RANK]=a[x].id,rank[a[x].id]=RANK;
    for (int i=0;i<26;i++)
        if (a[x].tree[i]!=0) dfs(a[x].tree[i]);
}

void build()
{
    for (int i=1;i<=s;i++)
        c[a[i].len]++;
    for (int i=1;i<=s;i++)
        c[i]+=c[i-1];
    for (int i=1;i<=s;i++)
        b[c[a[i].len]--]=i;
    for (int i=s;i>=1;i--)
    {
        int p=b[i];
        a[a[p].fa].tree[sx[a[p].id+a[a[p].fa].len-1]-'a']=p;
    }
    RANK=0;
    dfs(1);
}

LL Query(int x,int y)
{
    if (x==y) return a[x].len;
    if (a[x].len>a[y].len) return Query(a[x].fa,y);
    else return Query(x,a[y].fa);
}

int main()
{
    n=read(sx);n=strlen(sx);
    Insert(sx);
    build();
    for (int i=1;i<n;i++)
        print(sa[i]),putchar(' ');
    print(sa[n]);puts("");
    for (int i=1;i<n-1;i++)
        print(Query(d[sa[i]],d[sa[i+1]])),putchar(' ');
    if (n>1) print(Query(d[sa[n-1]],d[sa[n]])),puts("");
    return 0;
}

后缀自动机

最长公共子串(多串)

int n,m,s,b[500010],c[500010];
char sx[250010],sy[250010];
struct data
{
    int len,fa,letter[26],res,x;
}a[500010];
const int inf=500010;
void Extend(int x,int p)
{
    s++;int q=s;a[q].len=a[p].len+1;
    while (p!=0 && a[p].letter[x]==0)
        a[p].letter[x]=q,p=a[p].fa;
    if (p==0) {a[q].fa=1;return ;}
    int np=a[p].letter[x];
    if (a[np].len==a[p].len+1) a[q].fa=np;
    else
    {
        s++;int nq=s;a[nq].len=a[p].len+1;
        for (int i=0;i<26;i++)
            a[nq].letter[i]=a[np].letter[i];
        a[nq].fa=a[np].fa;a[np].fa=nq;a[q].fa=nq;
        while (p!=0&&a[p].letter[x]==np)
            a[p].letter[x]=nq,p=a[p].fa;
    }
}
void Insert(char x[])
{
    int y=strlen(x);
    s=1;int z=1;
    for (int i=0;i<y;i++)
        Extend(x[i]-'a',z),z=a[z].letter[x[i]-'a'];
    memset(c,0,sizeof(c));
    for (int i=1;i<=s;i++)
        a[i].res=a[i].len,c[a[i].len]++;
    for (int i=1;i<=s;i++)
        c[i]+=c[i-1];
    for (int i=1;i<=s;i++)
        b[c[a[i].len]--]=i;
}
void Query(char x[])
{
    int res=0,p=1,z,y=strlen(x);
    for (int i=0;i<y;i++)
    {
        z=x[i]-'a';
        while (p!=0&&a[p].letter[z]==0) p=a[p].fa,res=a[p].len;
        if (a[p].letter[z]!=0) res++,p=a[p].letter[z];
        else res=0,p=1;
        a[p].x=max(a[p].x,res);
    }
    for (int i=s;i>=1;i--)
    {
        p=b[i];
        a[a[p].fa].x=max(a[a[p].fa].x,a[p].x);
        a[p].res=min(a[p].res,a[p].x);
        a[p].x=0;
    }
}
int main()
{
    n=read(sx);
    Insert(sx);
    while (read(sy)) 
        Query(sy);
    int ans=0;
    for (int i=1;i<=s;i++)
        ans=max(ans,a[i].res);
    print(ans),puts("");
    return 0;
}

求第\(k\)小字符串

不同位置相同子串算多个

int n,m,s,b[1000010],c[1000010];
char sx[1000010],sy[1000010];
struct data
{
    int fa,letter[26];
    LL len,child,res;
}a[1000010];
void Extend(int x,int p)
{
    s++;int q=s;a[q].len=a[p].len+1;
    while (p!=0 && a[p].letter[x]==0)
        a[p].letter[x]=q,p=a[p].fa;
    if (p==0) {a[q].fa=1;return ;}
    int np=a[p].letter[x];
    if (a[np].len==a[p].len+1) a[q].fa=np;
    else
    {
        s++;int nq=s;a[nq].len=a[p].len+1;
        for (int i=0;i<26;i++)
            a[nq].letter[i]=a[np].letter[i];
        a[nq].fa=a[np].fa;a[np].fa=nq;a[q].fa=nq;
        while (p!=0&&a[p].letter[x]==np)
            a[p].letter[x]=nq,p=a[p].fa;
    }
}
void Insert(char x[])
{
    int y=strlen(x);
    s=1;int z=1;
    for (int i=0;i<y;i++)
        Extend(x[i]-'a',z),z=a[z].letter[x[i]-'a'],a[z].child++;
    memset(c,0,sizeof(c));
    for (int i=1;i<=s;i++)
        a[i].res=0,c[a[i].len]++;
    for (int i=1;i<=s;i++)
        c[i]+=c[i-1];
    for (int i=1;i<=s;i++)
        b[c[a[i].len]--]=i;
}
void init()
{
    for (int i=s;i>=1;i--)
    {
        int p=b[i];
        if (n==1) a[a[p].fa].child+=a[p].child;
        else a[p].child=1;
    }
    for (int i=s;i>=1;i--)
    {
        int p=b[i];a[p].res=a[p].child;
        for (int j=0;j<26;j++)
            a[p].res+=a[a[p].letter[j]].res;
    }
}
void Query(int y,int x)
{
    if (x<=0) return ;
    int i=0;
    while(i<26&&a[a[y].letter[i]].res<x)
        x-=a[a[y].letter[i]].res,i++;
    if (i==26) {puts("-1");return ;}
    else {putchar(i+'a'),Query(a[y].letter[i],x-a[a[y].letter[i]].child);}
}
int main()
{
    n=read(sx);
    Insert(sx);
    read(n);read(m);
    init();
    a[1].child=0;
    Query(1,m);
    return 0;
}

不同位置相同子串算一个

namespace SuffixAutomation {
const int MAXN = 180005;
struct Node {
    Node *next[26], *fa;
    int max, cnt;
    Node(int max = 0) : max(max) {}
    Node(int max, Node *p) {
        *this = *p, this->max = max;
    }
    inline void *operator new(size_t);
} pool[MAXN], *cur = pool, *root, *last;
inline void *Node::operator new(size_t) {
    return cur++;
}
inline void init() {
    root = last = new Node();
}
inline Node *extend(int c, Node *p) {
    Node *np = new Node(p->max + 1);
    while (p && !p->next[c]) p->next[c] = np, p = p->fa;
    if (!p) {
        np->fa = root;
    } else {
        Node *q = p->next[c];
        if (q->max == p->max + 1) {
            np->fa = q;
        } else {
            Node *nq = new Node(p->max + 1, q);
            q->fa = np->fa = nq;
            while (p && p->next[c] == q) p->next[c] = nq, p = p->fa;
        }
    }
    return np;
}
std::vector<Node *> buc[MAXN];
typedef std::pair<Node *, int> Pair;
std::vector<Pair> edge[MAXN];
/*预处理从每个节点出发,还有多少不同的子串可以到达。*/
inline void prepare(const char *s, const int n) {
    for (Node *p = pool; p != cur; p++) p->cnt = 1, buc[p->max].push_back(p);
    for (register int i = n; i >= 0; i--) {
        for (auto p : buc[i]) {
            for (register int j = 0; j < 26; j++) {
                if (p->next[j]) {
                    edge[p - pool].push_back(Pair(p->next[j], j));
                    /*这里是预处理,保证时间复杂度,并不是 endPos 集合,记录的是当前节点不同子串个数*/
                    p->cnt += p->next[j]->cnt;
                }
            }
        }
    }
}
inline void dfs(Node *p, int k) {
    if (--k == 0) return;
    for (auto i : edge[p - pool]) {
        if (i.first->cnt < k) {
            k -= i.first->cnt;
        } else {
            putchar('a' + i.second), dfs(i.first, k);
            break;
        }
    }
}
inline void solve() {
    static char s[MAXN];
    scanf("%s", s);
    register int n = strlen(s);
    init();
    for(register int i = 0; i < n; i++) last = extend(s[i] - 'a', last);
    prepare(s, n);
    register int q;
    scanf("%d", &q);
    for (register int i = 1, k; i <= q; i++) {
        scanf("%d", &k), dfs(root, k + 1), putchar('\n');
    }
}
}
int main() {
    SuffixAutomation::solve();
}

不同子串的出现次数

\(f[i]\)指长度为\(i\)的串出现次数的最大值,输出\(f[1]\)...\(f[n]\)

一个节点\(s\)的出现次数是\(endpos(s)\),一个节点的长度范围是\([len(s->fa)+1, len(s)]\)

namespace SuffixAutomation {
const int MAXN = 500005;
struct Node {
    Node *next[26], *fa;
    int max, endPosSize;
    Node(int max = 0) : max(max) {}
    Node(int max, Node *p) {
        *this = *p, this->max = max;
    }
    inline void *operator new(size_t);
} pool[MAXN], *cur = pool, *root, *last;
inline void *Node::operator new(size_t) {
    return cur++;
}
inline void init() {
    root = last = new Node();
}
inline Node *extend(int c, Node *p) {
    Node *np = new Node(p->max + 1);
    while (p && !p->next[c]) p->next[c] = np, p = p->fa;
    if (!p) {
        np->fa = root;
    } else {
        Node *q = p->next[c];
        if (q->max == p->max + 1) {
            np->fa = q;
        } else {
            Node *nq = new Node(p->max + 1, q);
            q->fa = np->fa = nq;
            while (p && p->next[c] == q) p->next[c] = nq, p = p->fa;
        }
    }
    return np;
}
inline void getEndPosSize(const char *s, const int n) {
    /*将Suffix Link 上的所有叶子节点的endPosSize设置为1*/
    Node *p = root;
    for (register int i = 0; i < n; i++)
        p = p->next[*s++ - 'a'], p->endPosSize++;
    /*按照 len 从大到小的顺序更新*/
    static std::vector<Node *> buc[MAXN];
    static int ans[MAXN];
    for (Node *p = pool; p != cur; p++) buc[p->max].push_back(p);
    for (register int i = n; i; i--) {
        for (auto p : buc[i]) {
            ans[i] = std::max(ans[i], p->endPosSize);    //这里做更新
            p->fa->endPosSize += p->endPosSize;
        }
    }
    for (register int i = n - 1; i; i--) ans[i] = std::max(ans[i], ans[i + 1]);    //这道题目的特殊优化
    for (register int i = 1; i <= n; i++) std::cout << ans[i] << "\n";
}
inline void solve() {
    init();
    static char s[MAXN];
    scanf("%s", s);
    register int n = strlen(s);
    for (register int i = 0; i < n; i++) 
        last = extend(s[i] - 'a', last);
    getEndPosSize(s, n);
}
}
int main() {
    SuffixAutomation::solve();
    return 0;
}

最长不重叠重复子串

“主题”是整个音符序列的一个子串,它需要满足如下条件:

1.长度至少为 5 个音符。

2.在乐曲中重复出现。(可能经过转调,“转调”的意思是主题序列中每个音符都被加上或减去了同一个整数值)

3.重复出现的同一主题不能有公共部分。

对于转调,做一次差分,然后回归此问题

namespace SuffixAutomation {
const int MAXN = 20005;
struct Node {
    Node *fa, *next[175];
    int max, minPos, maxPos;
    Node(int max = 0) : max(max), minPos(20000), maxPos(0), fa(NULL) {
        memset(next, 0, sizeof(next));
    }
    Node(int max, Node *p) {
        *this = *p, this->max = max;
    }
    inline void *operator new(size_t);
} pool[MAXN << 1], *cur = pool, *root, *last;
int n, a[MAXN];
std::vector<Node *> buc[MAXN];
inline void *Node::operator new(size_t) {
    return cur++;
}
inline void init() {
    cur = pool;
    root = last = new Node();
    for (register int i = 0; i <= n; i++) buc[i].clear();
}
inline Node *extend(int c, Node *p) {
    Node *np = new Node(p->max + 1);
    while (p && !p->next[c]) p->next[c] = np, p = p->fa;
    if (!p) {
        np->fa = root;
    } else {
        Node *q = p->next[c];
        if (q->max == p->max + 1) {
            np->fa = q;
        } else {
            Node *nq = new Node(p->max + 1, q);
            q->fa = np->fa = nq;
            while (p && p->next[c] == q) p->next[c] = nq, p = p->fa;
        }
    }
    return np;
}
inline int getAns() {
    register int res = 0;
    Node *p = root;
    p->minPos = p->maxPos = 0;
    for (register int i = 0; i < n; i++)
        p = p->next[a[i]], p->maxPos = p->minPos = i + 1;
    for (Node *i = pool; i != cur; i++) buc[i->max].push_back(i);
    for (register int i = n; i; i--) {
        for (register int j = 0; j < buc[i].size(); j++) {
            Node *tmp = buc[i][j];
            res = std::max(res, std::min(tmp->max, tmp->maxPos - tmp->minPos - 1));
            tmp->fa->minPos = std::min(tmp->fa->minPos, tmp->minPos);
            tmp->fa->maxPos = std::max(tmp->fa->maxPos, tmp->maxPos);
        }
    }
    return res < 4 ? 0 : res + 1;
}
inline void solve() {
    while (scanf("%d", &n), n) {
        for (register int i = 0; i < n; i++) scanf("%d", a + i);
        n--;
        for (register int i = 0; i < n; i++) a[i] = a[i + 1] - a[i] + 87;
        init();
        for (register int i = 0; i < n; i++) last = extend(a[i], last);
        std::cout << getAns() << "\n";
    }
}
}
int main() {
    SuffixAutomation::solve();
    return 0;
}

本质不同的子串个数

namespace SuffixAutomation {
const int MAXN = 50000;
struct Node {
    Node *fa, *next[128];
    int max;
    Node(int max = 0) : max(max), fa(NULL) {
        memset(next, 0, sizeof(next));
    }
    Node(int max, Node *q) {
        *this = *q, this->max = max;
    }
    inline void *operator new(size_t);
} pool[MAXN + 1 << 1], *cur = pool, *root, *last;
inline void *Node::operator new(size_t) {
    return cur++;
}
int ans;
inline void init() {
    cur = pool;
    ans = 0;
    root = last = new Node();
}
inline Node *extend(int c, Node *p) {
    Node *np = new Node(p->max + 1);
    while (p && !p->next[c]) p->next[c] = np, p = p->fa;
    if (!p) {
        np->fa = root;
    } else {
        Node *q = p->next[c];
        if (q->max == p->max + 1) {
            np->fa = q;
        } else {
            Node *nq = new Node(p->max + 1, q);
            q->fa = np->fa = nq;
            while (p && p->next[c] == q) p->next[c] = nq, p = p->fa;
        }
    }
    ans += np->max - np->fa->max;
    return np;
}
inline void solve() {
    register int n;
    scanf("%d", &n);
    for (register int i = 0; i < n; i++) {
        static char s[MAXN];
        scanf("%s", s);
        init();
        register int len = strlen(s);
        for (register int i = 0; i < len; i++) last = extend(s[i], last);
        std::cout << ans << "\n";
    }
}
}
int main() {
    SuffixAutomation::solve();
    return 0;
}

后缀平衡树

一棵根节点为\(1\)的树,每个节点代表一个字符

对于每个节点求这个节点到根节点路径上的不同子串的个数

输出出N个数,表示节点i到根节点路径上的不同子串的个数

const int N=1e5+10;
const int bas=31;
int hs[N],M[N];
int n,len,ans,Ans[N];
inline int read(){
    int f=1,x=0;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return f*x;
}
struct Suffix_Balanced_ScapeGoat_Tree{
    int lx[N],rx[N],rt,size[N],s[N],cnt,Q[N],tail;
    ll tag[N];
    int get(int i,int l){return hs[i]-hs[i-l]*M[l];}
    int qlcp(int x,int y){
        int l=0,r=min(x,y);
        while(l<r){
            int mid=(l+r)>>1;
            if(get(x,mid+1)==get(y,mid+1))l=mid+1;else r=mid;
        }
        return l;
    }
    inline bool cmp(int x,int y){int l=qlcp(x,y);return s[x-l]<s[y-l];}
    inline int merge(int x,int y){
        if(!x||!y)return x|y;
        if(size[x]>size[y]){size[x]+=size[y];rx[x]=merge(rx[x],y);return x;}
        else{size[y]+=size[x];lx[y]=merge(x,lx[y]);return y;}
    }
    inline int build(int ls,int rs,ll l,ll r){
        if(ls>rs)return 0;
        int mid=(ls+rs)>>1;ll midv=(l+r)>>1;int x=Q[mid];tag[x]=midv;
        lx[x]=build(ls,mid-1,l,midv);rx[x]=build(mid+1,rs,midv,r);
        size[x]=rs-ls+1;return x;
    }
    inline void dfs(int x){if(lx[x])dfs(lx[x]);Q[++tail]=x;if(rx[x])dfs(rx[x]);}
    inline int rebuild(int x,ll l,ll r){
        tail=0;dfs(x);return build(1,tail,l,r);
    }
    inline int ins(int x,ll l,ll r,int val){
        if(!x){
            size[++cnt]=1;lx[cnt]=rx[cnt]=0;tag[cnt]=(l+r)>>1;
            return cnt;
        }
        size[x]++;
        if(cmp(x,val)){
            rx[x]=ins(rx[x],tag[x],r,val);
            if(size[rx[x]]>0.65*size[x])x=rebuild(x,l,r);
        }
        else{
            lx[x]=ins(lx[x],l,tag[x],val);
            if(size[lx[x]]>0.65*size[x])x=rebuild(x,l,r);
        }
        return x;
    }
    inline int del(int x,int val){
        if(x==val)return merge(lx[x],rx[x]);
        size[x]--;
        if(tag[x]<tag[val])rx[x]=del(rx[x],val);
        else lx[x]=del(lx[x],val);
        return x;
    }
    inline int queryrk(int key){
        int x=rt,ans=0;
        while(1){
            int i=size[lx[x]]+1;
            if(key==x)return ans+i;
            if(tag[x]<tag[key])x=rx[x],ans+=i;else x=lx[x];
        }
    }
    inline int find(int key){
        int x=rt;
        while(1){
            int i=size[lx[x]]+1;
            if(key==i)return x;
            if(key>i)x=rx[x],key-=i;else x=lx[x];
        }
    }
    inline void del(int x){
        int rk=queryrk(x),y=find(rk-1),z=find(rk+1);
        ans-=qlcp(x,y)+qlcp(x,z)-qlcp(y,z);
        rt=del(rt,x);cnt--;len--;
    }
    inline void ins(int x){
        s[++len]=x;hs[len]=hs[len-1]*bas+x;
        rt=ins(rt,0,1LL<<62,len);
        if(len<3)return;
        int rk=queryrk(len),y=find(rk-1),z=find(rk+1);
        ans+=qlcp(len,y)+qlcp(len,z)-qlcp(y,z);
    }
}T;
char str[N];
struct Edge{int u,v,next;}G[N<<1];int head[N],tot=0;
inline void addedge(int u,int v){
    G[++tot].u=u;G[tot].v=v;G[tot].next=head[u];head[u]=tot;
    G[++tot].u=v;G[tot].v=u;G[tot].next=head[v];head[v]=tot;
}
inline void dfs(int u,int fa){
    T.ins(str[u]-'a'+1);Ans[u]=(len-1)*(len-2)/2-ans;
    for(int i=head[u];i;i=G[i].next){
        if(G[i].v!=fa)dfs(G[i].v,u);
    }
    T.del(len);
}
int main(){
    freopen("balsuffix.in","r",stdin);
    freopen("balsuffix.out","w",stdout);
    M[0]=1;
    for(int i=1;i<N;i++)M[i]=M[i-1]*bas;
    T.ins(27);T.ins(0);int T=read();
    while(T--){
        n=read();for(int i=1;i<=n;i++)head[i]=0;tot=0;
        for(int i=1;i<n;i++){
            int u=read(),v=read();addedge(u,v);
        }
        scanf("%s",str+1);
        dfs(1,0);
        for(int i=1;i<=n;i++)printf("%d\n",Ans[i]);
    }
}

Palindromic树

const int maxn = 300010*2;  
const int ALP = 26;  

struct PAM{  
    int next[maxn][ALP];  
    int fail[maxn] ;//fail指针,失配后跳转到fail指针指向的节点  
    int cnt[maxn] ; //表示节点i表示的本质不同的串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的) 
    int num[maxn] ; //表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数
    int len[maxn] ;//len[i]表示节点i表示的回文串的长度(一个节点表示一个回文串)
    int s[maxn] ;//存放添加的字符  
    int last ;//指向新添加一个字母后所形成的最长回文串表示的节点。
    int n ;//表示添加的字符个数。
    int p ;//表示添加的节点个数。

    int newnode(int l){  
        for(int i=0;i<ALP;i++)  
            next[p][i]=0;  
        cnt[p]=num[p]=0;  
        len[p]=l;  
        return p++;  
    }  
    void init(){  
        p = 0;  
        newnode(0);  
        newnode(-1);  
        last = 0;  
        n = 0;  
        s[n] = -1;  
        fail[0] = 1;  
    }  
    int get_fail(int x){  
        while(s[n-len[x]-1] != s[n]) x = fail[x];  
        return x;  
    }  
    void add(int c){  
        c = c-'a';  
        s[++n] = c;  
        int cur = get_fail(last);  
        if(!next[cur][c]){  
            int now = newnode(len[cur]+2);    //注意字符串长度为1的是由-1拓展过来的,要特判
            fail[now] = next[get_fail(fail[cur])][c];
            next[cur][c] = now;  
            num[now] = num[fail[now]] + 1;  
        }  
        last = next[cur][c];  
        cnt[last]++;  
    }  
    void count(){  
        for(int i=p-1;i>=0;i--)
            cnt[fail[i]] += cnt[i];  
    }  
}pam;

char s[maxn];  
int main(){  
    scanf("%s",s);  
    int len = strlen(s);  
    pam.init();  
    for(int i=0;i<len;i++)
    {  
        pam.add(s[i]);  
    }
    pam.count();  
    long long ret = 0;  
    for(int i=2;i<pam.p;i++)     //遍历所有的回文串
    {  
        ret = max((long long)pam.len[i]*pam.cnt[i],ret);  
    }  
    cout<<ret<<endl;  
    return 0;  
}  

遍历

对于 A 中的每个回文子串,B 中和该子串相同的子串个数的总和

ll dfs(int an,int bn){  
    ll ret = 0;  
    for(int i=0;i<ALP;i++) if(pam1.next[an][i]!=0 && pam2.next[bn][i]!=0)  
        ret += (ll)pam1.cnt[pam1.next[an][i]] * pam2.cnt[pam2.next[bn][i]]  
            + dfs(pam1.next[an][i],pam2.next[bn][i]);  
    return ret;  
}  

ll ret = dfs(0,0) + dfs(1,1);

最小表示法

给出一个循环字符串,输出最小字典序的开始下标

const int MAXN=100000+7;
char s[MAXN];
int n;
int get_minstring()
{
    int len=n;
    int i=0,j=1,k=0;
    while(i<len&&j<len&&k<len)
    {
        int t=s[(i+k)%len]-s[(j+k)%len];//t值为两个字符比较的结果,而(i+k)%len是因为i+k会超过len又因为是循环同构串所以要%len。同理(j+k)%len也是一样的道理。 
        if(t==0)
        k++;
        else
        {
            if(t>0)
            i+=k+1;
            else
            j+=k+1;
            if(i==j)
            j++;    
            k=0;
        }
    }
    return min(i,j);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%s",&n,s);
        printf("%d\n",get_minstring());
    }
} 

图论

k短路

Dijkstra

struct Edge
{
    int to;
    int value;
    int next;
    bool operator < (const Edge &t) const
    {
        return t.value < value;
    }
};
struct Edge1
{
    int to;
    int value;
    int next;
};
struct Node
{
    int to;
    int f, g;
    bool operator < (const Node &t) const
    {
        if(t.f==f)
            return t.g < g;
        return t.f < f;
    }
};
Edge edge[100010];  //存储边与原图反向的图
Edge1 edge1[100010];    //存储原图
int adj[1010], adj1[1010], visited[1010], dis[1010], edgeNum, edgeNum1;
int N, M;

void Dijkstra(int start)
{
    int k;
    Edge t, cur;
    priority_queue<Edge> PQ;
    for(k=0; k<N; k++)
    {
        visited[k] = 0;
        dis[k] = INT_MAX;
    }
    t.to = start;   //起始顶点
    t.next = -1;
    t.value = 0;
    dis[start] = 0; //自己到自己路径为0
    PQ.push(t);
    visited[start] = 1; //标记以入队
    while(!PQ.empty())
    {
        cur = PQ.top();  //出队
        PQ.pop();
        visited[cur.to] = 0;    //标记出队
        for(int tmp = adj[cur.to]; tmp != -1; tmp = edge[tmp].next)
        {
            if(dis[edge[tmp].to] > dis[cur.to] + edge[tmp].value)
            {
                dis[edge[tmp].to] = dis[cur.to] + edge[tmp].value;
                if(visited[edge[tmp].to] == 0)
                {
                    PQ.push(edge[tmp]);
                    visited[edge[tmp].to] = 1;
                }
            }
        }
    }
}

int A_star(int start, int end, int k)
{
    Node e, ne;
    int cnt = 0;
    priority_queue<Node> PQ;
    if(start==end)
        k++;
    if(dis[start]==INT_MAX)     //无法到达终点
        return -1;
    e.to = start;
    e.g = 0;
    e.f = e.g + dis[e.to];
    PQ.push(e);
    while(!PQ.empty())
    {
        e = PQ.top();
        PQ.pop();
        if(e.to==end)
            cnt++;  //第cnt短路
        if(cnt==k)
            return e.g;
        for(int i=adj1[e.to]; i!=-1; i=edge1[i].next)
        {
            ne.to = edge1[i].to;
            ne.g = e.g + edge1[i].value;
            ne.f = ne.g + dis[ne.to];
            PQ.push(ne);
        }
    }
    return -1;
}

void addEdge(int a, int b, int len) //反向图添加边
{
    edge[edgeNum].to = b;
    edge[edgeNum].next = adj[a];
    edge[edgeNum].value = len;
    adj[a] = edgeNum++;
}

void addEdge1(int a, int b, int len)    //原图添加边
{
    edge1[edgeNum1].to = b;
    edge1[edgeNum1].next = adj1[a];
    edge1[edgeNum1].value = len;
    adj1[a] = edgeNum1++;
}

int main()
{
    int a, b, len, i, s, t, k;
    while(scanf("%d %d", &N, &M)!=EOF)
    {
        for(i=0; i<N; i++)
        {
            adj[i] = -1;
            adj1[i] = -1;
        }
        for(edgeNum=i=0; i<M; i++)
        {
            scanf("%d %d %d", &a, &b, &len);
            addEdge1(a-1, b-1, len);    //构造原图
            addEdge(b-1, a-1, len); //构***向图
        }
        scanf("%d %d %d", &s, &t, &k);
        Dijkstra(t-1);  //求原图中各点到终点的最短路
        int ans = A_star(s-1, t-1, k);  //求第k短路
        printf("%d\n", ans);
    }
    return 0;
}

SPFA

#define INF 0x3f3f3f
#define maxn 1010
typedef pair<int, int> pii;
struct edge{int to, cost;};
vector<edge> G[maxn];//正向图,邻接表
vector<edge> rG[maxn];//反向图,邻接表
int s, t, k;
int n, m, ok;
 
int d[maxn], In_que[maxn];//反向spfa
void spfa(int s){
    memset(In_que, 0, sizeof(In_que));
    memset(d, INF, sizeof(d));
    queue<int> Q;
    Q.push(s);
    d[s] = 0;
    In_que[s] = 1;
    while (!Q.empty()){
        int u = Q.front();
        Q.pop();
        In_que[u] = 0;
        for (int i = 0; i < rG[u].size(); i++){
            edge e = rG[u][i];
            int v = e.to, dd = d[u] + e.cost;
            if (d[v] > dd){
                d[v] = dd;
                if (!In_que[v]){
                    Q.push(v);
                    In_que[v] = 1;
                }
            }
        }
    }
}
 
struct state{
    int u, g, h;
    bool operator < (const state &b) const{
        return g + h > b.g + b.h;
    }
    state(int a, int b, int c):u(a), g(b), h(c){}
};
int cnt[maxn], kd[maxn];
void Astar(){
    memset(cnt, 0, sizeof(cnt));
    priority_queue<state> Q;//open表
    Q.push(state(s, 0, d[s]));
    while (!Q.empty()){
        state cur = Q.top();
        Q.pop();
        int u = cur.u;
        cnt[u]++;
        if (cnt[u] == k && u == t){//终点第k次出队
            printf("%d\n", cur.g);
            ok = 1;
            break;
        }
        if (cnt[u] > k)//如果出队次数大于k,不用拓展了,因为一个点的第k短距离肯定是由周围点的
            //前k短距离更新得来
            continue;
        for (int i = 0; i < G[u].size(); i++){
            edge e = G[u][i];
            if (cnt[e.to] != k){//如果这个点不在closed表中
                Q.push(state(e.to, cur.g + e.cost, d[e.to]));
            }
        }
    }
}
 
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; i++){
        int u, v, c;
        scanf("%d%d%d", &u, &v, &c);
        G[u].push_back((edge){v, c});
        rG[v].push_back((edge){u, c});//反向建边
    }
    scanf("%d%d%d", &s, &t, &k);
    if (s == t) k++;//题目特殊要求。。
    spfa(t);
    Astar();
    if (!ok)//如果没有第k短路
        printf("-1\n");
    return 0;
}

生成树

瓶颈生成树

每次询问\((u, v)\),在这两个点间找一条路径使得这个路径上最大的边权最小

#define N 50010
#define M 100010
struct Edge {
    int from, to, dis, nex;
}edge[M<<1], hehe[M<<1];
bool cmp(Edge a, Edge b){return a.dis < b.dis;}
int head[N], edgenum;
void add(int u, int v, int d){
    Edge E = {u,v,d,head[u]};
    edge[edgenum] = E;
    head[u] = edgenum++;
}
int f[N];
int find(int x){return x==f[x] ? x : f[x] = find(f[x]);}
bool Union(int x, int y){
    int fx = find(x), fy = find(y);
    if(fx == fy)return false;
    if(fx>fy)swap(fx,fy);
    f[fx] = fy;
    return true;
}
int fa[N][20], dep[N], cost[N][20]; //cost[u][i]表示u点到u的第2^i次父亲 路径上最大边权
void bfs(int root){
    queue<int>q;
    fa[root][0] = root; dep[root] = 0;
    cost[root][0] = 0;
    q.push(root);
    while(!q.empty()) {
        int u = q.front(); q.pop();
        for(int i = 1; i < 20; i++) {
            fa[u][i] = fa[fa[u][i-1]][i-1];
            cost[u][i] = max(cost[u][i-1], cost[fa[u][i-1]][i-1]);
        }
        for(int i = head[u]; ~i; i = edge[i].nex) {
            int v = edge[i].to; if(v == fa[u][0])continue;
            dep[v] = dep[u]+1; fa[v][0] = u; cost[v][0] = edge[i].dis;
            q.push(v);
        }
    }
}
int work(int x, int y){
    int ans = 0;
    if(dep[x] < dep[y]) swap(x, y);
    for(int i = 0; i < 20; i++) if((dep[x]-dep[y])&(1<<i)) {
        ans = max(ans, cost[x][i]);
        x = fa[x][i];
    }
    if(x == y)return ans;
    for(int i = 19; i >= 0; i--)if(fa[x][i]!=fa[y][i]) {
        ans = max(ans, max(cost[x][i], cost[y][i]));
        x = fa[x][i], y = fa[y][i];
    }
    ans = max(ans, cost[x][0]);
    ans = max(ans, cost[y][0]);
    return ans;
}
int main(){
    int i, j, u, v, d, query, n, m, fir = 0;
    while(~scanf("%d %d",&n,&m)){
        if(fir++)puts("");
        memset(head, -1, sizeof head); edgenum = 0;
        for(i = 1; i <= n; i++) f[i] = i;
        for(i = 0; i < m; i++){
            read(hehe[i].from); read(hehe[i].to); read(hehe[i].dis);
        }
        sort(hehe, hehe + m, cmp);
        int siz = 0;
        for(int i = 0; i < m && siz < n; i++) {
            if(Union(hehe[i].from, hehe[i].to)){
                siz++;
                add(hehe[i].from, hehe[i].to, hehe[i].dis);
                add(hehe[i].to, hehe[i].from, hehe[i].dis);
            }
        }
        bfs(1);
        read(query);
        while(query--){
            read(u); read(v);
            printf("%d\n", work(u, v));
        }
    }
    return 0;
}

k小生成树

输出\(\displaystyle\left(\sum_{k=1}^{K}{k \cdot V(k)}\right) \bmod 2^{32}\)\(V(k)\)是k小生成树的权值和)

const int MAXN = 1000+100;
const int M = 3000;
const int MO=(1LL<<32);
int n,m,k,ca,cb;
struct node{
    int v,w,nex;
}e[M*2];
int EN,head[M*2],fa[MAXN],A[100010],B[100010];
struct edge{
    int u,v,w;
    edge(int u,int v,int w):u(u),v(v),w(w){}
    edge(){}
};
vector<edge> ve;
void init()
{
    memset(head,-1,sizeof head);
    EN = 0;
    ve.clear();
    ca=0;cb=0;
    for(int i=0;i<=n;i++)fa[i]=i;
}
void add(int u,int v,int w)
{
    e[EN].v=v,e[EN].w=w,e[EN].nex=head[u];
    head[u]=EN++;
}
int fand(int x)
{
    return fa[x]==x?x:fa[x]=fand(fa[x]);
}
bool dfs(int u,int f,int en)
{
    if(u==en)return true;
    for(int i=head[u];~i;i=e[i].nex)
    {
        if(i==f) continue;
        if(dfs(e[i].v,i^1,en))
        {
            B[cb++]=e[i].w;
            return true;
        }
    }
    return false;
}
bool cmp(int a,int b)
{
    return a>b;
}
void merg()
{
    if(ca > cb) swap(ca,cb),swap(A,B);
    int siz = min(k,ca*cb);
    priority_queue<pair<int,int> >q;
    for(int i=0;i<ca;i++) q.push(make_pair(A[i]+B[0],0));
    ca=siz;
    for(int i=0;i<siz;i++)
    {
        pair<int,int>p = q.top();q.pop();
        A[i] = p.first;
        if(p.second+1<cb) q.push(make_pair(p.first-B[p.second]+B[p.second+1],p.second+1));
    }
}
main()
{
    ios::sync_with_stdio(false);
    int tt=0;
    while(cin>>n>>m)
    {
        init();
        int sum=0;
        int u,v,w;
        while(m--)
        {
            cin>>u>>v>>w;
            if(fand(u)!=fand(v))
            {
                add(u,v,w);
                add(v,u,w);
                fa[fand(u)]=fand(v);
            }else
            {
                ve.push_back(edge(u,v,w));
            }
            sum+=w;
        }cin>>k;
        for(auto E : ve)
        {
            cb=0;
            B[cb++]=E.w;
            dfs(E.u,-1,E.v);
            sort(B,B+cb,cmp);
            if(ca==0)
            {
                for(int i=0;i<cb;i++)
                    A[i]=B[i];
                ca=cb;
            }else
                merg();
        }
        int ans=0;
        for(int i=0;i<ca;i++)
        {
            ans+=(sum-A[i])*(i+1);
            ans%=MO;
        }
        if(ans==0) ans=sum;
        ans%=MO;
        cout<<"Case #"<<++tt<<": "<<ans<<endl;
    }
}    

虚树

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e6+10;
#define FOR(i,L,R) for(register int i=(L);i<=(R);++i)
#define REP(j,R,L) for(register int j=(R);j>=(L);--j)
int n;
struct Graph{//处理最初图,求lca 
    vector<int>G[N];
    int cnt,xu[N],dep[N],p[N][27];
    inline int lca(int x,int y){
        if(dep[x]<dep[y])swap(x,y);
        REP(i,log2(dep[x]),0)if(dep[x]-(1<<i)>=dep[y])x=p[x][i];
        if(x==y)return y;
        REP(i,log2(dep[x]),0)if(p[x][i]^p[y][i])x=p[x][i],y=p[y][i];
        return p[x][0];
    }
    inline void ST(){
        int k=log2(n);
        FOR(j,1,k)FOR(i,1,n)if(~p[i][j-1])p[i][j]=p[p[i][j-1]][j-1];
    }
    inline void dfs(int u,int fa,int depth){
        xu[u]=++cnt;
        int sz=G[u].size()-1;
        FOR(i,0,sz)if(G[u][i]^fa)dfs(G[u][i],p[G[u][i]][0]=u,dep[G[u][i]]=depth+1);
    }
    inline void init(){
        memset(p,-1,sizeof(p));
        scanf("%d",&n);
        FOR(i,1,n-1){
            int x,y;scanf("%d%d",&x,&y);
            G[x].push_back(y);
            G[y].push_back(x);
        }
    }
}g;
bool cmp(int a,int b){
    return g.xu[a]<g.xu[b];
}
struct Fake_Tree{//应该是这么翻译吧…… 
    struct edge{
        int to,next,w;
    }a[N];//这里不用vector是因为清空很慢 
    int m,h[N],sig[N],flag[N];
    int cnt,top,stk[N];
    #define next a[p].next
    #define to a[p].to
    #define w a[p].w
    inline void solv(){
        …… 
        FOR(i,1,m)flag[sig[i]]=0;//记得清空。。。 
    }
    inline void add(int u,int v){
        if(u==v)return ;//注意这里很重要! 
        a[++cnt]=(edge){v,h[u],g.dep[v]-g.dep[u]};h[u]=cnt;//因为每次维护一条树链且是单位长度,所以u和v的距离就是他们之间的深度 
    }
    inline void init(){
        scanf("%d",&m);
        FOR(i,1,m)scanf("%d",sig+i);//读入key point
        sort(sig+1,sig+m+1,cmp);
        FOR(i,1,m)flag[sig[i]]=1;//标记
        //注意有的题可以删去一些关键点 
        cnt=top=0;
        stk[++top]=1;
        FOR(i,1,m){
            int now=sig[i],lca=g.lca(now,stk[top]);
            while(g.dep[lca]<g.dep[stk[top-1]]){//因为是维护树链,所以可用深度判断 
                add(stk[top-1],stk[top]);
                --top;
            }add(lca,stk[top]);
            stk[top]=lca;//直接覆盖,注意上面使用top-1判断 
            if(stk[top]!=now)stk[++top]=now;//判断稳妥一点 
        }while(--top)add(stk[top],stk[top+1]);//最后剩在栈里的一条链 
    }
}z;
int main(){
    g.init();    //读入原图
    g.dfs(1,g.p[1][0]=0,g.dep[1]=1);
    g.ST();
    int Case;scanf("%d",&Case);
    while(Case--){
        z.init();//读入关键点
        z.solv();
    }return 0;
}

欧拉回路

第一行一个整数 t,表示子任务编号。t∈{1,2},如果 t=1 则表示处理无向图的情况,如果t=2 则表示处理有向图的情况。

第二行两个整数 n,m,表示图的结点数和边数。

接下来 m 行中,第 i 行两个整数 vi,ui,表示第 i 条边(从 1 开始编号)。保证 1≤vi,ui≤n。

1.如果 t=1 则表示 vi 到 ui 有一条无向边。

2.如果 t=2 则表示 vi 到 ui 有一条有向边。

图中可能有重边也可能有自环。

int T,n,m,p,d[100010],ru[100010],chu[100010],ans[200010],h[100010];
vector<int> b;
struct data
{
    int x,id;
    data(int a=0,int b=0):x(a),id(b){}
};
vector<data> a[100010];
bool v[200010];

void dfs1(int x,int y)
{
    if (a[x].size()>h[x])
    {
        while (v[abs(a[x][h[x]].id)])
        {
            h[x]++;
            if (h[x]==a[x].size()) break;
        }
        if (a[x].size()>h[x])
        {
            int i=a[x][h[x]].x,j=a[x][h[x]].id;
            h[x]++;v[abs(j)]=1;
            if (i!=y) dfs1(i,y);
            ans[++p]=j;
        }   
    }
    if (a[x].size()>h[x])
    {
        while (v[abs(a[x][h[x]].id)])
        {
            h[x]++;
            if (h[x]==a[x].size()) break;
        }
        if (a[x].size()>h[x]) dfs1(x,x);
    }
}

void dfs2(int x,int y)
{
    if (a[x].size()>h[x])
    {
        int i=a[x][h[x]].x,j=a[x][h[x]].id;
        h[x]++;
        if (i!=y) dfs2(i,y);
        ans[++p]=j;
    }
    if (a[x].size()>h[x]) dfs2(x,x);
}

int main()
{
    read(T);
    if (T==1)
    {
        read(n);read(m);
        int x,y;
        for (int i=1;i<=m;i++)
            read(x),read(y),d[x]++,d[y]++,a[x].push_back(data(y,i)),a[y].push_back(data(x,-i));
        x=0;
        for (int i=1;i<=n;i++)
            if (d[i]%2==1) {puts("NO");return 0;}
            else if (d[i]>0) x=i;
        memset(h,0,sizeof(h));
        p=0;dfs1(x,x);
        if (p<m) {puts("NO");return 0;}
        puts("YES");
        for (int i=m;i>1;i--)
            print(ans[i]),putchar(' ');
        if (m>0) print(ans[1]),puts("");
    }
    else if (T==2)
    {
        read(n);read(m);
        int x,y;
        for (int i=1;i<=m;i++)
            read(x),read(y),chu[x]++,ru[y]++,a[x].push_back(data(y,i)); 
        x=0;
        for (int i=1;i<=n;i++)
            if (ru[i]!=chu[i]) {puts("NO");return 0;}
            else if (ru[i]>0) x=i;
        memset(h,0,sizeof(h));
        p=0;dfs2(x,x);
        if (p<m) {puts("NO");return 0;}
        puts("YES");
        for (int i=m;i>1;i--)
            print(ans[i]),putchar(' ');
        if (m>0) print(ans[1]),puts("");
    }
    return 0;
}

斯坦纳树

\(n(n\le 30)\)个城市,\(m(m\le 1000)\)条路,给出四对城市,是每对联通的最小边权代价。

const int maxn=30;
map<string,int>hsh;
queue<int>q;
bool vis[maxn];
int hash1[maxn],dp[1<<8][maxn],dis[maxn][maxn],num[maxn],ans[1<<4];

int main(){
    int n,m;
    while(scanf("%d%d",&n,&m),n || m){
        memset(dp,-1,sizeof dp);
        memset(dis,-1,sizeof dis);
        memset(ans,-1,sizeof ans);
        memset(hash1,-1,sizeof hash1);
        hsh.clear();
        for(int i=0;i<n;i++){string s;cin>>s;hsh[s]=i;dis[i][i]=0;dp[0][i]=0;}
        while(m--){string a,b;int c;cin>>a>>b>>c;int x=hsh[a],y=hsh[b];dis[x][y]=dis[y][x]=dis[x][y]==-1?c:min(dis[x][y],c);}
        for(int i=0;i<8;i++){
            string s;cin>>s;
            hash1[hsh[s]]=((i&1)<<2)|(i>>1);
            dp[1<<hash1[hsh[s]]][hsh[s]]=0;
            dp[0][hsh[s]]=-1;
            num[hash1[hsh[s]]]=hsh[s];     
        }
        for(int mask=0;mask< 1<<8;mask++){
            for(int i=0;i<n;i++){
                if(hash1[i]!=-1 && !(mask>>hash1[i]&1))continue;
                for(int mask1=(mask-1)&mask;mask1;mask1=(mask1-1)&mask){
                    int mask2=mask^mask1 |((hash1[i]!=-1)?1<<hash1[i]:0);
                    if(dp[mask1][i]!=-1 && dp[mask2][i]!=-1)dp[mask][i]=dp[mask][i]==-1?dp[mask2][i]+dp[mask1][i]:min(dp[mask][i],dp[mask2][i]+dp[mask1][i]);
                }
                while(!q.empty())q.pop();
                for(int i=0;i<n;i++)if(dp[mask][i]!=-1){q.push(i);vis[i]=true;}else vis[i]=false;
                while(!q.empty()){
                    int u=q.front();q.pop();vis[u]=false;
                    for(int v=0;v<n;v++)
                        if(dis[u][v]!=-1){
                            if(dp[mask][v]==-1 || dp[mask][v]>dp[mask][u]+dis[u][v]){
                                dp[mask][v]=dp[mask][u]+dis[u][v];
                                if(!vis[v]){q.push(v);vis[v]=true;}
                            }
                        }
                }
            }
            for(int mask=0;mask< 1<<4;mask++){
                for(int i=0;i<4;i++)
                    if(mask>>i & 1){ans[mask]=dp[mask<<4 | mask][num[i]];break;}
                for(int mask1=(mask-1)&mask;mask1;mask1=(mask1-1)&mask){
                    int mask2=mask^mask1;
                    if(ans[mask1]!=-1 && ans[mask2]!=-1)ans[mask]=ans[mask]==-1?(ans[mask1]+ans[mask2]):min(ans[mask],ans[mask1]+ans[mask2]);
                }
            }
        }
        printf("%d\n",ans[(1<<4)-1]);
    }
    return 0;
}

最小树形图

\(1\)向其他节点单向连通的最小代价。给出每个点的坐标。

#define INF 2000000000
#define MAXN 105
#define MAXM 1005
#define eps 1e-4
using namespace std;
typedef double type;
struct Point
{
    double x, y;
}p[MAXN];
struct node
{
    int u, v;
    type w;
}edge[MAXN * MAXN];
int pre[MAXN], id[MAXN], vis[MAXN], n, m;
type in[MAXN];
double dis(Point a, Point b)
{
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
type Directed_MST(int root, int V, int E)
{
    type ret = 0;
    while(true)
    {
        //1.找最小入边
        for(int i = 0; i < V; i++)
           in[i] = INF;
        for(int i = 0; i < E; i++)
        {
            int u = edge[i].u;
            int v = edge[i].v;
            if(edge[i].w < in[v] && u != v)
              {pre[v] = u; in[v] = edge[i].w;}
        }
        for(int i = 0; i < V; i++)
        {
            if(i == root) continue;
            if(in[i] == INF) return -1;//除了根以外有点没有入边,则根无法到达它
        }
        //2.找环
        int cnt = 0;
        memset(id, -1, sizeof(id));
        memset(vis, -1, sizeof(vis));
        in[root] = 0;
        for(int i = 0; i < V; i++) //标记每个环
        {
            ret += in[i];
            int v = i;
            while(vis[v] != i && id[v] == -1 && v != root)  //每个点寻找其前序点,要么最终寻找至根部,要么找到一个环
            {
                vis[v] = i;
                v = pre[v];
            }
            if(v != root && id[v] == -1)//缩点
            {
                for(int u = pre[v]; u != v; u = pre[u]) 
                    id[u] = cnt;
                id[v] = cnt++;
            }
        }
        if(cnt == 0) break; //无环   则break
        for(int i = 0; i < V; i++)
            if(id[i] == -1) id[i] = cnt++;
        //3.建立新图
        for(int i = 0; i < E; i++)
        {
            int u = edge[i].u;
            int v = edge[i].v;
            edge[i].u = id[u];
            edge[i].v = id[v];
            if(id[u] != id[v]) edge[i].w -= in[v];
        }
        V = cnt;
        root = id[root];
    }
    return ret;
}
int main()
{
    while(scanf("%d%d", &n, &m) != EOF)
    {
        for(int i = 0; i < n; i++) 
           scanf("%lf%lf", &p[i].x, &p[i].y);
        for(int i = 0; i < m; i++)
        {
            scanf("%d%d", &edge[i].u, &edge[i].v);
            edge[i].u--;
            edge[i].v--;
            if(edge[i].u != edge[i].v) edge[i].w = dis(p[edge[i].u], p[edge[i].v]);
            else edge[i].w = INF; //去除自环
        }
        type ans = Directed_MST(0, n, m);
        if(ans == -1) printf("poor snoopy\n");
        else printf("%.2f\n", ans);
    }
    return 0;
}

动态最短路

删边

\(n(n\le 200000)\)个点\(m(m\le 200000)\)条边,给出起点和终点,\(q(q\le 200000)\)个询问每次删除一条边(询问独立),求最短路径。

#define N 200005
#define INF (1ll<<60)
using namespace std;
typedef long long LL;
namespace Segment_Tree {
    struct Node {
        Node* ch[2];
        int l,r;
        LL v;
        Node() {}
        Node(int _l,int _r):l(_l),r(_r),v(INF) {
            ch[0]=ch[1]=NULL;
        }
        void* operator new(size_t) {
            static Node *mempool,*C;
            if(mempool==C) mempool=(C=new Node[1<<20])+(1<<20);
            return C++;
        }
    }*root;
    void Init(Node*& o,int l,int r) {
        o=new Node(l,r);
        if(l==r) return ;
        int mid=l+r>>1;
        Init(o->ch[0],l,mid), Init(o->ch[1],mid+1,r);
        return ;
    }
    void Change(Node* o,int l,int r,LL v) {
        if(o->l==l && o->r==r) {
            o->v=min(o->v,v);
            return ;
        }
        int mid=o->l+o->r>>1;
        if(r<=mid) Change(o->ch[0],l,r,v);
        else if(l>mid) Change(o->ch[1],l,r,v);
        else Change(o->ch[0],l,mid,v), Change(o->ch[1],mid+1,r,v);
        return ;
    }
    LL Query(Node* o,int pos) {
        if(o->l==o->r) return o->v;
        int mid=o->l+o->r>>1,dir=pos<=mid?0:1;
        return min(Query(o->ch[dir],pos),o->v);
    }
}
#define ST Segment_Tree
struct Data {
    int ord;
    LL val;
    Data() {}
    Data(int _ord,LL _val):ord(_ord),val(_val) {}
    bool operator < (const Data& rhs) const {
        return val>rhs.val;
    }
};
struct Edge {
    int from,to,nxt,val;
    Edge() {}
    Edge(int _from,int _to,int _nxt,int _val):
        from(_from),to(_to),nxt(_nxt),val(_val) {}
}e[N*2];
int n,m,tot,S,T,Q,top,fir[N],fromS[N],fromT[N],seq[N],pos[N];
LL distS[N],distT[N],ans[N];
void Add_Edge(int u,int v,int c) {
    e[++tot]=Edge(u,v,fir[u],c), fir[u]=tot;
    e[++tot]=Edge(v,u,fir[v],c), fir[v]=tot;
    return ;
}
void Dijkstra(int st,LL dist[]) {
    static bool k[N];
    priority_queue<Data> q;
    for(int i=1;i<=n;++i) dist[i]=INF, k[i]=false;
    dist[st]=0, q.push(Data(st,0));
    while(!q.empty()) {
        Data tmp=q.top(); q.pop();
        int x=tmp.ord;
        if(k[x]) continue;
        k[x]=true;
        for(int i=fir[x];~i;i=e[i].nxt) {
            if(dist[e[i].to]<=dist[x]+e[i].val) continue;
            dist[e[i].to]=dist[x]+e[i].val;
            q.push(Data(e[i].to,dist[e[i].to]));
        }
    }
    return ;
}
void Find_path() {
    int x=S;
    while(x!=T) {
        seq[++top]=x;
        pos[x]=top;
        for(int i=fir[x];~i;i=e[i].nxt) {
            if(distT[e[i].to]+e[i].val>distT[x]) continue;
            x=e[i].to;
            break;
        }
    }
    seq[++top]=x, pos[x]=top;
    return ;
}
void bfs(int ord,LL dist[],int from[]) {
    int st=seq[ord];
    queue<int> q;
    q.push(st);
    while(!q.empty()) {
        int x=q.front(); q.pop();
        for(int i=fir[x];~i;i=e[i].nxt)
            if(!from[e[i].to] && dist[e[i].to]==dist[x]+e[i].val)
                from[e[i].to]=ord, q.push(e[i].to);
    }
    return ;
}
void calc_from() {
    for(int i=1;i<=top;++i) fromS[seq[i]]=fromT[seq[i]]=i;
    for(int i=1;i<=top;++i) bfs(i,distS,fromS);
    for(int i=top;i;--i) bfs(i,distT,fromT);
    return ;
}
int main() {
    memset(fir,-1,sizeof fir), tot=-1;
    scanf("%d%d",&n,&m);
    for(int i=1,x,y,z;i<=m;++i)
        scanf("%d%d%d",&x,&y,&z), Add_Edge(x,y,z);
    scanf("%d%d",&S,&T);
    Dijkstra(S,distS), Dijkstra(T,distT);
    Find_path(), calc_from();
    ST::Init(ST::root,1,top-1);
    for(int i=0;i<=tot;++i) {
        int from=e[i].from,to=e[i].to;
        if(pos[from] && pos[to] && abs(pos[from]-pos[to])==1) continue;
        if(fromS[from] && fromT[to] && fromS[from]<fromT[to]) ST::Change(ST::root,fromS[from],fromT[to]-1,distS[from]+e[i].val+distT[to]);
    }
    for(int i=1;i<top;++i) ans[i]=ST::Query(ST::root,i);
    for(scanf("%d",&Q);Q;--Q) {
        int x,y;
        scanf("%d%d",&x,&y);
        if(pos[x]>pos[y]) swap(x,y);
        LL tmp;
        if(pos[x] && pos[y] && pos[x]+1==pos[y]) tmp=ans[pos[x]];
        else tmp=distS[T];
        printf(tmp==INF ? "Infinity\n" : "%lld\n",tmp);
    }
    return 0;
}

网络流