板子合集,持续更新中...

树上倍增求LCA

const int N = 1e5+5;
int lca[N][20];
int dep[N];
void dfs(int now,int f,int depth){///初始时dfs(根节点,0,1);
    lca[now][0] = f;
    dep[now] = depth;
    for (int i=head[now];i!=0;i=edge[i].last){//链式前向星
        int v = edge[i].to;
        if (v==f) continue; 
        dfs(v,now,depth+1);
    }
}

void getst(int n){
    for (int j=1;(1<=0;i--){
        if (lca[x][i]!=lca[y][i]) x = lca[x][i],y = lca[y][i];
    }
    return lca[x][0];
}

最小表示法

///求所有循环同构中字典序最小或者最大
int getmin(int len)
{
    int i=0,j=1,k=0;
    while (i0) j += k+1;
            else i += k+1;
            if (i==j) j++;
            k = 0;
        }
    }
    return i

Manacher

const int maxn=1000010;
char str[maxn];//原字符串
char tmp[maxn<<1];//转换后的字符串
int Len[maxn<<1];
//转换原始串
int INIT(char *st)
{
    int i,len=strlen(st);
    tmp[0]='@';///开头加一个不等于#也不等于字符串的字符,这样就不用判断左边越界了,那么右边万一比到n+1怎么办呢?有\0吗?不,\0在n处,解决办法看16行
    for(i=1;i<=2*len;i+=2)
    {
        tmp[i]='#';
        tmp[i+1]=st[i/2];
    }
    tmp[2*len+1]='#';
    tmp[2*len+2]='$';///结尾搞一个不是@也不是#的字符
    tmp[2*len+3]=0;
    return 2*len+1;//返回转换字符串的长度
}
//Manacher算法计算过程
int MANACHER(char *st,int len)
{
     int mx=0,ans=0,po=0;//mx即为当前计算回文串最右边字符的最大值
     for(int i=1;i<=len;i++)
     {
         if(mx>i)
         Len[i]=min(mx-i,Len[2*po-i]);//在Len[j]和mx-i中取个小
         else
         Len[i]=1;//如果i>=mx,要从头开始匹配
         while(st[i-Len[i]]==st[i+Len[i]])
         Len[i]++;
         if(Len[i]+i>mx)//若新计算的回文串右端点位置大于mx,要更新po和mx的值
         {
             mx=Len[i]+i;
             po=i;
         }
         ans=max(ans,Len[i]);
     }
     return ans-1;//返回Len[i]中的最大值-1即为原串的最长回文子串额长度
}

Len[st+ed]-1>=ed-st+1判断区间是否回文


EXKMP

const int maxn=100010;   //字符串长度最大值
int nt[maxn],ex[maxn]; //ex数组即为extend数组
///预处理计算next数组
void GETNEXT(char *str)
{
    int i=0,j,po,len=strlen(str);
    nt[0]=len;///用自己作为后缀与自己匹配
    while(i+1

KMP

#include
const int N = 1e6+5;
using namespace std;
int nt[N];
char s[N];
char ss[N];
int KMP() ///求大串中有几个子串
{
    int len=strlen(s);
    int t=strlen(ss);
    nt[0]=-1;
    int cnt=0;
    ///构造next数组
    for (int i=0,j=-1; i>s){
        cin>>ss;
        cout<

字典树---普通版本

int trie[maxnode][sigma_size];///maxnode表示节点数,maxnode=单词数*每个单词最大长度(最坏情况每个单词组合都不一样)
                              ///sigma_size 表示一个节点最多延伸出多少各节点,也就是字符种类
///  trie[i][j] 的值表示 i节点指向j这个数字对应字母 的节点的位置
bool val[maxnode];            ///为真表示当前节点是一个单词的结束
int sz;
 
int idx(char ch) { return ch-'a';}//将字母转换成对应数字,如果还有大写,标点的话要更麻烦些
 
void init(int x){
    val[x] = false;
    memset(trie[x],0,sizeof trie[x]);
}
 
void insert(char *s){///插入
    int u = 0;
    for (int i=0;s[i];i++){
        int v = idx(s[i]);
        if (!trie[u][v]){
            init(sz);///初始化多组输入时上一次残留的数据
            trie[u][v] = sz++;
        }
        u = trie[u][v];
    }
    val[u] = true;
}
 
bool query(char *s){
    int u = 0;
    for (int i=0;s[i];i++){
        int v = idx(s[i]);
        if (!trie[u][v]) return false; ///这个节点还没有开辟
        u = trie[u][v];
    }
    return true;
}

领接表存字典树

#include
#include
#define ll long long
 
using namespace std;
 
const int N = 4000*1001 + 5;
 
struct node
{
    int son;
    int right;
    int sum;
    char ch;
}trie[N];
int id;
ll ans;
 
void init()
{
    ans = 0;
    id = 1;
    trie[0].right = 0;
    trie[0].son = 0;
    trie[0].sum = 0;
}
 
void insert(char *s)
{
    int u = 0,j;
    int len = strlen(s);
    for (int i=0;i<=len;i++){
        bool flag = false;
 
        for (j=trie[u].son;j!=0;j=trie[j].right){
            if (s[i]==trie[j].ch){
                flag = true;
                break;
            }
        }
 
        if (!flag){
            j = id++;
            trie[j].right = trie[u].son;
            trie[u].son = j;
            trie[j].ch = s[i];
 
            trie[j].son = 0;
            trie[j].sum = 0;
        }
 
        ans += (trie[u].sum+trie[j].sum);
        if (i==len) trie[j].sum++;
 
        trie[u].sum++;
        u = j;
    }
}
 
int main()
{
    int n;
    char in[1010];
    for (int kca=1;scanf("%d",&n),n;kca++){
        init();
        while (n--) scanf("%s",in),insert(in);
        printf("Case %d: %lld\n",kca,ans);
    }
}

后缀数组求两个串LCS----最长公共子串

#include
#include
#include
#define debug(x) printf("----Line%s----\n",#x)
 
using namespace std;
 
const int N = 1e5+5;
 
int wa[N<<1],wb[N<<1],sa[N<<1],rnk[N<<1],cnt[N<<1],height[N<<1];
char s[N<<1];
 
void build(int n,int m)
{
    int *x=wa,*y=wb,p,i,j;
    for (i=0;i=0;i--) sa[--cnt[x[i]]] = i;
 
    for (j=1,p=1;p=j) y[p++] = sa[i]-j;
 
        for (i=0;i=0;i--) sa[--cnt[x[y[i]]]] = y[i];
 
        swap(x,y); 
        x[sa[0]] = 0;
        p = 1;
        for (i=1;ilen) || (sa[i]>len && sa[i-1]

后缀数组查询两段区间LCP,ST维护height

#include
#include
#include
#include
#define debug(x) printf("----line %s----\n",#x)
using namespace std;

const int N = 2e5 + 5;

int sa[N],wa[N],wb[N],rnk[N],cnt[N],height[N];
char s[N];
int st[N][20];

void buildsa(int n,int m)///m是初始字符种类,可以偏大
{
    int *x = wa,*y = wb,p,i,j;
    for (int i=0;i=0;i--) sa[--cnt[x[i]]] = i;

    for (j=1,p=1;p=j) y[p++] = sa[i]-j;///得出根据第二关键字排名的

        for (int i=0;i=0;i--) sa[--cnt[x[y[i]]]] = y[i];
    
        swap(x,y);
        x[sa[0]] = 0;
        p = 1;
        for (i=1;ir) swap(l,r);

        if (l==r){
            ans = min(b-a+1,d-c+1);
        }
        else {
            ans = min(min(b-a+1,d-c+1),query(l+1,r));
        }
        printf("%d\n",ans);
    }
    return 0;
}

大数运算

const int power = 4;      ///每次运算的位数为10的power次方,在这里定义为了方便程序实现
const int base = 10000;      ///10的power次方。

const int MAXL = 200;    ///MAXL*POWER = 表示的数的范围


char a[MAXL], b[MAXL];
struct num
{
    int a[MAXL];///如果POWER>4,不修改为修改为LL乘法会爆
    num() { memset(a, 0, sizeof(a)); }                      ///初始化
    num(char *s)                                            ///将一个字符串初始化为高精度数
    {
        memset(a, 0, sizeof(a));
        int len = strlen(s);
        a[0] = (len+power-1) / power;                       ///数的长度
        for (int i=0, t=0, w; i < len ;w *= 10, ++i)
        {
            if (i % power == 0) { w = 1, ++t; }
            a[t] += w * (s[i]-'0');
        }
    }

    void add(int k) { if (k || a[0]) a[ ++a[0] ] = k; }     ///在末尾添加一个数,除法的时候要用到,不是当前数+k!!
    void re() { reverse(a+1, a+a[0]+1); }                   ///把数反过来,除法的时候要用到
    void print()                                            ///打印此高精度数
    {
        printf("%d", a[ a[0] ]);///先打印最高位,为了压位 或者 该高精度数为0 考虑
        for (int i = a[0]-1;i > 0;--i)
            printf("%0*d", power, a[i]);///这里"%0*d", power的意思是,必须输出power位,不够则前面用0补足
        printf("\n");
    }
} p,q,ans;


bool operator < (const num &p, const num &q)              ///判断小于关系,除法的时候有用
{
    if (p.a[0] < q.a[0]) return true;
    if (p.a[0] > q.a[0]) return false;
    for (int i = p.a[0];i > 0;--i)
    {
        if (p.a[i] != q.a[i]) return p.a[i] < q.a[i];
    }
    return false;
}

num operator + (const num &p, const num &q)               ///加法,不用多说了吧,模拟一遍,很容易懂
{
    num c;
    c.a[0] = max(p.a[0], q.a[0]);
    for (int i = 1;i <= c.a[0];++i)
    {
        c.a[i] += p.a[i] + q.a[i];
        c.a[i+1] += c.a[i] / base;
        c.a[i] %= base;
    }
    if (c.a[ c.a[0]+1 ]) ++c.a[0];
    return c;
}

num operator - (const num &p, const num &q)               ///减法,也不用多说,模拟一遍,很容易懂
{
    num c = p;
    for (int i = 1;i <= c.a[0];++i)
    {
        c.a[i] -= q.a[i];
        if (c.a[i] < 0) { c.a[i] += base; --c.a[i+1]; }
    }
    while (c.a[0] > 0 && !c.a[ c.a[0] ]) --c.a[0];
    ///我的习惯是如果该数为0,那么他的长度也是0,方便比较大小和在末尾添加数时的判断。
    return c;
}

num operator * (const num &p, const num &q)
    ///乘法,还是模拟一遍。。其实高精度就是模拟人工四则运算!
{
    num c;
    c.a[0] = p.a[0]+q.a[0]-1;
    for (int i = 1;i <= p.a[0];++i)
        for (int j = 1;j <= q.a[0];++j)
        {
            c.a[i+j-1] += p.a[i]*q.a[j];
            c.a[i+j] += c.a[i+j-1] / base;
            c.a[i+j-1] %= base;
        }
    if (c.a[ c.a[0]+1 ]) ++c.a[0];
    return c;
}

num operator / (const num &p, const num &q)               ///除法,这里我稍微讲解一下
{
    num x, y;
    for (int i = p.a[0];i >= 1;--i)                       ///从最高位开始取数
    {
        y.add(p.a[i]);             ///把数添到末尾(最低位),这时候是高位在前,低位在后
        y.re();                    ///把数反过来,变为统一的存储方式:低位在前,高位在后
        while ( !(y < q) )         ///大于等于除数的时候,如果小于的话,其实答案上的该位就是初始的“0”
            y = y - q, ++x.a[i];   ///看能减几个除数,减几次,答案上该位就加几次。
        y.re();                    ///将数反过来,为下一次添数做准备
    }
    x.a[0] = p.a[0];
    while (x.a[0] > 0 && !x.a[x.a[0]]) --x.a[0];
    return x;
}

线性求1~n的逆元

p = k*i+r

k*i+r === 0 mod p,然后两边同乘以r^-1*i^-1

k*r^-1 + i^-1 === 0 mod p

inv[i] = -k * (1/r) mod p

inv[i] = (p-[p/i])*inv[p%i]%p

inv[1] = 1;
for (ll i=2;i<=n;i++)
    inv[i] = (mod-mod/i)*inv[mod%i]%mod;

excrt(扩展中国剩余定理)

第28行有修改,之前可能爆int了

先理清几个东西:

因为ax + by = c有解--->c是gcd(a,b)的整数倍--->求解出ax+by=gcd(a,b)的x,y都乘以c/gcd(a,b)即可

所以ax+by=1有解--->互质--->gcd(a,b) = 1

求解一次线性同余方程组

x === a1(mod m1)

x === a2(mod m2)

......

x = k1*m1+a1 = k2*m2+a2

先求解 m1*k1 - m2*k2 = a2-a1

求解出特解k1,得到 x = k1*m1+a1为满足第一第二个式子的特解,此时第一第二个式子通解为XX = x + LCM(m1,m2)*k

即类似于x = ki*mi+ai的形式,这个通解的式子继续和下一项重复上述步奏

void exgcd(ll a,ll b,ll &x,ll &y)
{
    if (b==0){x = 1;y = 0;}
    else{
        exgcd(b,a%b,y,x);
        y = y - a/b*x;
    }
}

ll gcd(ll a,ll b)
{
    return b==0? a:gcd(b,a%b);
}

int main()
{
    ll a1,a2,r1,r2,x,y;
    int n;
    while (~scanf("%d",&n)){
        scanf("%lld %lld",&a1,&r1);///x === a1 mod r1
        bool f = 1;
        for (int i=2;i<=n;i++){
            scanf("%lld %lld",&a2,&r2);///a1*x + r1 = a2*y + r2
            ll r = gcd(a1,-a2);        ///a1*x - a2*y = r2-r1
            if ((r2-r1)%r!=0){f = 0;continue;}///无解
            exgcd(a1,-a2,x,y);///算出来的是a1*x-a2*y = gcd(a1,a2)的解
            ll p = a2/gcd(a1,a2);
            x *= (r2-r1)/r; x = (x%p+p)%p;///求得实际x
            ///如果x>a2,x = xx + a2
            ///a1*(xx+a2) + a2*y  =c 有解,那么a1*xx + a2*(y+a1)=c有解
            
            ///对于ax+by=c,如果有整数解x,y && x大于b,一定存在x小于b的整数解
            
            ///    X = (a1*x+r1) + LCM(a1,a2)*k
            ///    X = ri        + ai*x 
            r1 = a1*x+r1;
            a1 = a1/gcd(a1,a2)*a2;
            r1 %= a1;///这一步没毛病
        }
        if (!f) puts("-1");
        else printf("%lld\n",r1);
    }
    return 0;
}

回文树

来自:https://blog.csdn.net/u013368721/article/details/42100363

#include

using namespace std;

const int maxn = 3e5+5;
const int ALP = 26;

struct PAM{ // 每个节点代表一个回文串
    int next[maxn][ALP]; // next指针,参照Trie树
    int fail[maxn]; // fail失配后缀链接
    int cnt[maxn]; // 此回文串出现个数
    int num[maxn];
    int len[maxn]; // 回文串长度
    int s[maxn]; // 存放添加的字符
    int last; //指向上一个字符所在的节点,方便下一次add
    int n; // 已添加字符个数
    int p; // 节点个数
    int l[maxn],r[maxn];//i节点代表的回文串第一次出现最左左右字符是第几个(不是下标)

    int newnode(int w){//新建一个节点,长度初始化为这个节点代表的回文串长度
        for(int i=0;i=0;i--)
            cnt[fail[i]] += cnt[i];
    }
}pam;

回文树前段插入

const int maxn = 1e5+5;
const int ALP = 26;

struct PAM{ // 每个节点代表一个回文串
    int next[maxn][ALP]; // next指针,参照Trie树
    int fail[maxn]; // fail失配后缀链接
    int num[maxn];
    int len[maxn]; // 回文串长度
    int s[maxn<<1]; // 存放添加的字符
    int l_last,r_last; //指向上一个字符所在的节点,方便下一次add
    int l,r; //r-l+1表示现在添加的字符数量
    int p; // 节点个数
    ll cnt;//记录当前回文串总数

    int newnode(int w){//新建一个节点,长度初始化为这个节点代表的回文串长度
        for(int i=0;i

快读模板:

找不到原文放不了链接了。

#include

using namespace std;

namespace FastI{
    const int SIZE = 1 << 16;
    char buf[SIZE], str[64];
    int l = SIZE, r = SIZE;
    int read(char *s) {
        while (r) {
            for (; l < r && buf[l] <= ' '; l++);
            if (l < r) break;
            l = 0, r = int(fread(buf, 1, SIZE, stdin));
        }
        int cur = 0;
        while (r) {
            for (; l < r && buf[l] > ' '; l++) s[cur++] = buf[l];
            if (l < r) break;
            l = 0, r = int(fread(buf, 1, SIZE, stdin));
        }
        s[cur] = '\0';
        return cur;
    }
    template
    bool read(type &x, int len = 0, int cur = 0, bool flag = false) {
        if (!(len = read(str))) return false;
        if (str[cur] == '-') flag = true, cur++;
        for (x = 0; cur < len; cur++) x = x * 10 + str[cur] - '0';
        if (flag) x = -x;
        return true;
    }
    template 
    type read(int len = 0, int cur = 0, bool flag = false, type x = 0) {
        if (!(len = read(str))) return false;
        if (str[cur] == '-') flag = true, cur++;
        for (x = 0; cur < len; cur++) x = x * 10 + str[cur] - '0';
        return flag ? -x : x;
    }
} using FastI::read;

int main() {
    //freopen("C:/Users/DELL/Desktop/input.txt", "r", stdin);
    //freopen("C:/Users/DELL/Desktop/output.txt", "w", stdout);
    int a[1000];
    while (read(a[1])){printf("%d",a[1]+1);}
    return 0;
}

AC自动机

const int maxnode = 1e6+5;//模式串数量*长度
const int ALP = 26;//字符种类数

struct AC_am
{
    queueque;
    int sz;
    int trie[maxnode][ALP];
    int fail[maxnode];
    int last[maxnode];
    int val[maxnode];//储存当前节点信息,如是否为单词节点等等

    int newnode(int x){
        memset(trie[x],0,sizeof trie[x]);
        val[x] = 0;
        return sz++;
    }

    void init(){
        newnode(sz = 0);
    }

    int idx(char ch){//实际字符串转化为字典树对应节点,根据题目做出具体改变
        return ch-'a';
    }

    void insert(char *s){
        int u = 0;
        for (int i=0;s[i];i++){
            int c = idx(s[i]);
            if (!trie[u][c]){
                trie[u][c] = newnode(sz);
            }
            u = trie[u][c];
        }
        val[u]++;
    }

    void build(){
        fail[0] = 0;
        for (int c=0;c

矩阵快速幂:

矩阵构造方法:写出两层递推式子,递推式有几项就构造几×几

const int matsize = 30;///矩阵最大大小
ll temp[matsize][matsize],ans[matsize][matsize];

int ms;///实际矩阵大小,每次矩阵快速幂之前根据矩阵大小修改
void multy(ll a[][matsize],ll b[][matsize])
{
    memset(temp,0,sizeof temp);
    for (int i=0;i>=1;
        multy(a,a);
    }
    for (int i=0;i

Floyd 算法:

作用:求任意两点最短路

时间:O(V^3)

#define for0(i,a,b) for (int i=a;i

dp[i][j]表示两个点之间可以用1~k-1作为中途点时的最短路径,输入取最小值防止有权值不同的重边

判断有无负环检查是否有dp[i][i]<0,有负环的话就没有最短路径了


堆优化dijkstra 算法:

作用:求单源最短路

时间:O(E*logE)

const int N = 1e5+5;///点的数量
const int M = 5e5+5;///边的数量
 
struct Edge//储存边
{
    int to;
    int last;
    int w;
}edge[M];
int head[N],id;
 
void add(int u,int v,int w)//建从u->v,权值为w的边
{
    edge[id].to = v;
    edge[id].w = w;
    edge[id].last = head[u];
    head[u] = id++;
}
 
void init()//建边前的初始化
{
    memset(head,0,sizeof head);
    id = 1;
}
 
struct node//储存点
{
    int now;
    int w;//到达now节点的这条边的权值
    bool operator < (const node& a)const{//***比较方式要和自己想的反过来***
        return w>a.w;
    }
};
 
bool vis[N];//是否求出最短路径
 
void dijkstra()
{
    memset(vis,0,sizeof vis);
    priority_queueque;
    int root = 1;//单元最短路径的源点
    que.push({root,0});
    while (!que.empty()){
        node now = que.top();que.pop();
        if (vis[now.now]) continue;
        vis[now.now] = true;
        /*
        当前点now记录了这个点到源点的最短距离,问题可以在这儿处理
        */
        for (int i=head[now.now];i!=0;i=edge[i].last){
            que.push({edge[i].to,edge[i].w+now.w});//***权值记得要加now.w***
        }
    }
}

不能处理有负权的边的问题,否则当前连到树上的点不一定是最短路径。

跑完dijkstra后相当于建了一棵包含所有节点的树,树上所有点到源点的距离最近。


Bellman_ford 算法

作用:求带负边的单源最短路/判负环

时间:O(V*E)

#define INF 0x3f3f3f3f
const int N = 1e4+5;
const int M = 5e4+5;
 
struct Edge
{
    int to,last,w;
}edge[M];
 
int id,head[N];
 
void add(int u,int v,int w)
{
    edge[id].to = v;
    edge[id].w = w;
    edge[id].last = head[u];
    head[u] = id++;
}
 
void init()
{
    id = 1;
    memset(head,0,sizeof head);
}
 
int dis[N];
 
void Bellman_ford(int V)//V表示节点数
{
    int root = 1;//若只是判断负环,随便取一个源点即可
    memset(dis,INF,sizeof dis);
    dis[root] = 1;
    for (int k=1;kdis[u]+edge[i].w){
                    dis[v] = dis[u] + edge[i].w;
                    /*
                    若要判断负环,将最外层循环修改为k<=V,并判断k==V时是否还有被修改的点
                    有则证明有负环,因为有负环会一直更新
                    */
                }
            }
    /*
    求得的dis[i]即为i到root的最短路径
    */    
}

第k次更新dis[i]相当于在更新:i沿着边反向走k步途中能到达的所有点形成的这个图中,i距离源点的最短路径

上一次我们求出了k-1步下每个点的单元最短路径,因此我们用所有能直接一步到达i的节点去更新即可。

当然有些点其实在第k次被更新好了,然后又去更新了别人,这样对那个被更新的点最终单源最短路的结果没有影响,就是可能提前获取了答案。

显然更新完V-1步,所有点的单源最短路都正确了,因为V-1步已经包含整张图了。还能更新说明有负环。


SPFA 算法(队列优化的Bellman_ford)

作用:求单源最短路/判负环

时间:O(V*E)  (一般情况远低于)

const int N = 1e4+5;
const int M = 5e4+5;
 
struct Edge
{
    int to,last,w;
}edge[M];
 
int id,head[N];
 
void add(int u,int v,int w)
{
    edge[id].to = v;
    edge[id].w = w;
    edge[id].last = head[u];
    head[u] = id++;
}
 
void init()
{
    id = 1;
    memset(head,0,sizeof head);
}
 
int val[N];//记录该节点被更新多少次
int dis[N];//记录单源最短路
 
void SPFA(int V)//V表示节点数
{
    memset(dis,INF,sizeof dis);
    memset(val,0,sizeof val);
 
    int root = 1;//若只用于判负环,源点可以随便取
    bool flag = false;//是否有负环,初始化为无
    dis[root] = 0;
    /*
    下面注释掉的四行是SPFA的优化,节点数多的时候一般来说是会优化...
    就是先让dis[]小的去松弛,这样可以让进队列的数更少
    */
    //dequeque;
    //que.pb(root);
    queueque;
    que.push(root);
 
    while (!que.empty()){
        int now = que.front();que.pop_front();
        int v;
        for (int i=head[now];i!=0;i=edge[i].last){
            v = edge[i].to;
            if (dis[v]>dis[now]+edge[i].w){
                dis[v] = dis[now] + edge[i].w;
                //que.pb(v);
                //if (dis[que.front()]

优化原理是未被松弛成功的点一定不会对下一轮松弛产生影响,因此用队列保留被松弛过的点去更新即可。


后缀自动机

const int MAXN = 250000+5;
const int ALP = 26;

struct SAM
{
    int trie[MAXN<<1][ALP];
    int len[MAXN<<1];
    int fa[MAXN<<1];
    int sz,las;

    void init(){
        newnode(las=sz=0);
        fa[0] = -1;
    }

    int newnode(int x){
        memset(trie[x],0,sizeof trie[x]);
        len[x] = 0;
        return sz++;
    }

    int idx(char ch){
        return ch-'a';
    }

    void add(char ch){
        int c = idx(ch);
        int p = las;
        int np = newnode(sz);
        las = np;
        len[np] = len[p]+1;
        for(;~p && !trie[p][c];p=fa[p]) trie[p][c] = np;/**这里循环停下来其实很关键的,停下来的话证明上一个后缀+C的对应的串存在,那个玩意儿就是现在的最长后缀了,不会更长了,否则之前就找到末尾+c有指向的点了*/
        if (p==-1) fa[np] = 0;
        else {
            int q = trie[p][c];
            if (len[q]==len[p]+1) fa[np] = q;
            else {
                int nq = newnode(sz);
                fa[nq] = fa[q];
                for0(i,0,ALP) trie[nq][i] = trie[q][i];
                len[nq] = len[p]+1;
                fa[np] = fa[q] = nq;
                for (;~p && trie[p][c]==q;p=fa[p]) trie[p][c] = nq;/**之前所有连到q的现在全部连到nq,因为要连到最短后缀,只需证明+C小于等于nq即可,显然的*/
            }
        }
    }

    int find(char *s){
        int u=0,maxlen=0,nowlen=0;
        for (int i=0;s[i];i++){
            int c = idx(s[i]);
            while (~u && !trie[u][c]){
                u = fa[u];
                if (~u) nowlen = len[u];
            }
            if (u==-1) {nowlen = u = 0;continue;}
            u = trie[u][c];
            nowlen++;
            maxlen = max(maxlen,nowlen);
        }
        return maxlen;
    }
}sam;

后缀自动机转后缀树转后缀数组

来自:https://blog.csdn.net/lvzelong2014/article/details/79006541

#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N = 1e6 + 10;
int sa[N], rank[N], s[N], n;
void readchar() {
    char ch = getchar(); int x = 0;
    while(ch < 'a' || ch > 'z') ch = getchar();
    while(ch >= 'a' && ch <= 'z') {s[++x] = ch - 'a'; ch = getchar();}
    n = x;
}
struct SAM {
    int ch[N][26], pos[N], len[N], fa[N], last, sz;
    bool val[N];
    SAM() {last = ++sz;}
    void Extend(int c, int po) {
        int p = last, np = last = ++sz;
        pos[np] = po;///********后缀起点位置,构造后缀树用的
        val[np] = true; ///********是否为后缀起点节点,构造后缀数组的时候用的
        len[np] = len[p] + 1;
        for(;p && !ch[p][c]; p = fa[p]) ch[p][c] = np;
        if(!p) fa[np] = 1;
        else {
            int q = ch[p][c];
            if(len[q] == len[p] + 1) fa[np] = q;
            else {
                int nq = ++sz; len[nq] = len[p] + 1;
                memcpy(ch[nq], ch[q], sizeof(ch[q]));
                fa[nq] = fa[q];
                pos[nq] = pos[q];
                fa[q] = fa[np] = nq;
                for(;ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
            }
        }
    }
}sam;

struct Suffix_Tree {
    int ch[N][26], pos[N], tp;
    bool val[N];
    void Add(int u, int v, int c) {ch[u][c] = v;}
    void Build() {
        for(int i = 1;i <= sam.sz; ++i) val[i] = sam.val[i], pos[i] = sam.pos[i];
        for(int i = 2;i <= sam.sz; ++i)
            Add(sam.fa[i], i, s[pos[i] + sam.len[sam.fa[i]]]);
    }
    void Dfs(int u) {
        if(val[u]) sa[rank[pos[u]] = ++tp] = pos[u];
        for(int i = 0 ;i < 26; ++i)
        if(ch[u][i]) Dfs(ch[u][i]);
    }
}suftree;

int main() {
    readchar();
    for(int i = n; i >= 1; --i) sam.Extend(s[i], i);
    suftree.Build();
    suftree.Dfs(1);
    for(int i = 1;i <= n; ++i) printf("%d ", sa[i]);
    putchar('\n');
    return 0;
}

Treap

插入与找第k小(提醒一下引用不要漏掉)

#include

using namespace std;

struct Treap
{
    Treap* ch[2];
    int val;
    int pri;
    int cnt;

    Treap(int x){ch[0] = ch[1] = NULL; pri = rand(); val = x;cnt = 1;}

    void maintain(){
        cnt = 1;
        if (ch[0]!=NULL) cnt += ch[0]->cnt;
        if (ch[1]!=NULL) cnt += ch[1]->cnt;
    }
};

void rotate(Treap* &rt,int d)
{
    Treap* k = rt->ch[d^1];
    rt->ch[d^1] = k->ch[d];
    k->ch[d] = rt;
    rt->maintain();
    k->maintain();
    rt = k;
}

void insert(Treap* &rt,int x)
{
    if (rt==NULL){rt = new Treap(x);}
    else {
        int d = (x < rt->val)? 0:1 ; 
        insert(rt->ch[d],x);
        if (rt->ch[d]->pri > rt->pri) rotate(rt,d^1);
    }
    rt->maintain();
}

int find(Treap* rt,int k)
{
    if (rt->ch[0]==NULL) ccnt = 1;
    else ccnt = 1 + rt->ch[0]->cnt;
    if (k==ccnt) return rt->val;
    else if (kch[0],k);
    else return find(rt->ch[1],k-ccnt);
}

int main()
{
    Treap* root = NULL;
    insert(root,1);
    insert(root,3);
    insert(root,5);
    printf("%d\n",find(root,2));
}

笛卡尔树

const int N = 1e5+5;

int a[N],n;//用于构建的数组
int sta[N],root;//栈,根节点
int ch[N][2],l[N],r[N];//树,比自己大的最左最右下标

void build(){
    int tot = -1;
    for1(i,1,n){
        ch[i][0] = ch[i][1] = 0;//初始化
        int nowtot = tot;//用于检验栈内是否弹出了元素,否则sta[tot+1]不知道连到什么鬼玩意
        while (tot>=0 && a[sta[tot]]>a[i]) tot--;
        if (tot>=0) ch[sta[tot]][1] = i;
        if (nowtot>tot) ch[i][0] = sta[tot+1];
        sta[++tot] = i;
    }
    root = sta[0];
}

void dfs(int now){//获取l,r
    if (ch[now][0]) dfs(ch[now][0]);
    l[now] = ch[now][0] ? l[ch[now][0]]:now;
    if (ch[now][1]) dfs(ch[now][1]);
    r[now] = ch[now][1] ? r[ch[now][1]]:now;
}

无旋treap

#include

using namespace std;

#define ll long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i pri[y]){
            ch[x][1] = merge(ch[x][1],y);
            maintain(x);
            return x;
        }
        else {
            ch[y][0] = merge(x,ch[y][0]);
            maintain(y);
            return y;
        }
    }

    void split(int rt,int v,int& x,int& y){
        if (!rt) x = y = 0;
        else {
            if (val[rt] <= v){
                x = rt;
                split(ch[rt][1],v,ch[rt][1],y);
                maintain(x);
            }
            else {
                y = rt;
                split(ch[rt][0],v,x,ch[rt][0]);
                maintain(y);
            }
        }
    }

    void insert(int v){
        int x,y;
        split(root,v,x,y);
        root = merge(merge(x,newnode(v)) , y);
    }

    void remove(int v){
        int x,y,z;
        split(root,v-1,x,y);
        split(y,v,y,z);
        y  = merge(ch[y][0],ch[y][1]);
        root = merge(merge(x,y),z);
    }

    int rank(int v){
        int x,y;
        split(root,v-1,x,y);
        int ans = cnt[x]+1;
        root = merge(x,y);
        return ans;
    }

    int KTH(int k){return val[kth(root,k)];}
    int kth(int rt,int k){
        if (k<=cnt[ch[rt][0]]) return kth(ch[rt][0],k);
        k -=  cnt[ch[rt][0]] + 1;
        if (k<=0) return rt;
        else return kth(ch[rt][1],k);
    }

    int pre(int v){
        int x,y;
        split(root,v-1,x,y);
        int ans = val[kth(x,cnt[x])];
        root = merge(x,y);
        return ans;
    }

    int suc(int v){
        int x,y;
        split(root,v,x,y);
        int ans = val[kth(y,1)];
        root = merge(x,y);
        return ans;
    }
}ftp;

int main()
{
    ftp.init();
    int m,op,x;
    scanf("%d",&m);
    while (m--){
        scanf("%d %d",&op,&x);
        if (op==1) ftp.insert(x);
        if (op==2) ftp.remove(x);
        if (op==3) printf("%d\n",ftp.rank(x));
        if (op==4) printf("%d\n",ftp.KTH(x));
        if (op==5) printf("%d\n",ftp.pre(x));
        if (op==6) printf("%d\n",ftp.suc(x));
    }
    return 0;
}

无旋treap维修数列(无旋treap登峰造极之操作)

struct fhq_treap
{
    int ch[maxn][2];
    int pri[maxn];
    int cnt[maxn];
    int val[maxn];
    int sum[maxn];
    int l[maxn],r[maxn],mcs[maxn];
    int rev[maxn],upd[maxn];
    int sta[maxn];
    int root;
    int nextpos[maxn],st,ed;///储存下一个可以用的节点编号

    void init(){
        st = 1,ed = 1;
        root = 0;
        l[0] = r[0] = mcs[0] = -INF;
        for (int i=1;i<=500005;i++) nextpos[i] = i;
    }

    int newnode(int v){
        int sz = nextpos[st++]; if (st==500006) st = 1;
        ch[sz][0] = ch[sz][1] = 0;
        pri[sz] = rand();
        cnt[sz] = 1;
        val[sz] = sum[sz] = l[sz] = r[sz] = mcs[sz] = v;
        rev[sz] = 0;
        upd[sz] = 0;
        return sz;
    }

    void use_for_reverse(int rt){
        if (!rt) return ;
        rev[rt] ^= 1;
        swap(l[rt],r[rt]);
    }

    void use_for_update(int rt,int c){
        if (!rt) return ;
        upd[rt] = 1;
        val[rt] = c;
        sum[rt] = c*cnt[rt];
        l[rt] = r[rt] = mcs[rt] = max(c,sum[rt]);
    }

    void update(int rt){
        cnt[rt] = 1 + cnt[ch[rt][0]] + cnt[ch[rt][1]];
        sum[rt] = val[rt] + sum[ch[rt][0]] + sum[ch[rt][1]];
        l[rt] = max(max(l[ch[rt][0]],sum[ch[rt][0]]+val[rt]),sum[ch[rt][0]]+val[rt]+l[ch[rt][1]]);
        r[rt] = max(max(r[ch[rt][1]],sum[ch[rt][1]]+val[rt]),sum[ch[rt][1]]+val[rt]+r[ch[rt][0]]);
        mcs[rt] = max(max(mcs[ch[rt][0]],mcs[ch[rt][1]]),max(0,r[ch[rt][0]])+val[rt]+max(0,l[ch[rt][1]]));
    }

    void push_down(int rt){
        if (!rt) return ;
        if (rev[rt]){
            use_for_reverse(ch[rt][0]);
            use_for_reverse(ch[rt][1]);
            rev[rt] = 0;
            swap(ch[rt][0],ch[rt][1]);
        }
        if (upd[rt]){
            use_for_update(ch[rt][0],val[rt]);
            use_for_update(ch[rt][1],val[rt]);
            upd[rt] = 0;
        }
    }

    int merge(int x,int y){
        if (!x || !y) return x+y;
        push_down(x),push_down(y);
        if (pri[x] < pri[y]){///维护大根堆
            ch[y][0] = merge(x,ch[y][0]);
            update(y);
            return y;
        }
        else {
            ch[x][1] = merge(ch[x][1],y);
            update(x);
            return x;
        }
    }

    int ccnt;
    void split(int rt,int k,int& x,int& y){
        if (!rt) x = y = 0;
        else {
            push_down(rt);
            ccnt = cnt[ch[rt][0]] + 1;
            if (k>=ccnt){///***这边需要一个等号,思考方法是判断此时根+左儿子是否属于前k个
                x = rt;
                split(ch[rt][1],k-ccnt,ch[rt][1],y);
            }
            else {
                y = rt;
                split(ch[rt][0],k,x,ch[rt][0]);
            }
            update(rt);
        }
    }

    int build(int n){
        int x;
        int tot = -1,st_tot;
        for (int i=1;i<=n;i++){
            read(x);
            int now = newnode(x);
            st_tot = tot;
            while (tot>=0){
                if (pri[sta[tot]] < pri[now]){///维护大根堆
                    update(sta[tot]);
                    tot--;
                }
                else {
                    ch[sta[tot]][1] = now;
                    break;
                }
            }
            if (tot < st_tot) ch[now][0] = sta[tot+1];
            sta[++tot] = now;
        }
        while (tot>=0) update(sta[tot--]);
        return sta[0];
    }

    void insert(int pos,int tot){
        int x,y;
        split(root,pos,x,y);
        x = merge(x,build(tot));
        root = merge(x,y);
    }

    void remove(int pos,int tot){
        int x,y,z;
        split(root,pos-1,x,y);
        split(y,tot,y,z);
        del(y);
        root = merge(x,z);
    }

    void update(int pos,int tot,int c){
        int x,y,z;
        split(root,pos-1,x,y);
        split(y,tot,y,z);
        use_for_update(y,c);
        root = merge(x,merge(y,z));
    }

    void reverse(int pos,int tot){
        int x,y,z;
        split(root,pos-1,x,y);
        split(y,tot,y,z);
        use_for_reverse(y);
        root = merge(x,merge(y,z));
    }

    int getsum(int pos,int tot){
        if (pos<=0) return 0;
        int x,y,z;
        split(root,pos-1,x,y);
        split(y,tot,y,z);
        int ans = sum[y];
        root = merge(x,merge(y,z));
        return ans;
    }

    int getmcs(){
        return mcs[root];
    }

    void del(int rt){
        if (!rt) return ;
        if (ch[rt][0]) del(ch[rt][0]);
        if (ch[rt][1]) del(ch[rt][1]);
        nextpos[ed++] = rt;
        if (ed==500006) ed = 1;
    }
/*  检查数组情况
    void DFS(int rt){
        if (!rt) return ;
        push_down(rt);
        if (ch[rt][0]) DFS(ch[rt][0]);
        printf("now = %d,val = %d,lmax = %d,rmax = %d\n",rt,val[rt],l[rt],r[rt]);
        if (ch[rt][1]) DFS(ch[rt][1]);
    }
*/
}ftp;

动态开点线段树

求逆序

#include
 
using namespace std;
 
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i>1
#define tl tree[rt].l
#define tr tree[rt].r
 
const int N = 5e5+5;
const int maxn = 500000*32 + 5;
const int MAXR = 1e9;
 
struct node
{
    int l,r;
    int sum;
}tree[maxn];
int sz;
 
void init(){sz = 2;}
 
void push_up(int rt)
{
    tree[rt].sum = tree[tl].sum + tree[tr].sum;
}
 
void update(int p,int& rt,int l,int r)
{
    //printf("update[%d,%d]\n",l,r);
    if (!rt){
        rt = sz++;
        tl = tr = tree[rt].sum = 0;
    }
    if (l==r){
        tree[rt].sum++;
        return;
    }
    mid;
    if (p<=m) update(p,tl,l,m);
    else update(p,tr,m+1,r);
    push_up(rt);
}
 
ll query(int L,int R,int rt,int l,int r)
{
    //printf("query[%d,%d]\n",l,r);
    if (!rt) return 0;
    if (L<=l && r<=R) return tree[rt].sum;
    ll ans = 0;
    mid;
    if (L<=m) ans += query(L,R,tl,l,m);
    if (R>m) ans += query(L,R,tr,m+1,r);
    return ans;
}
 
int main()
{
    init();
    int n;
    scanf("%d",&n);
    ll ans = 0;
    int root = 1;
    for1(i,1,n){
        int x;
        scanf("%d",&x);
        if (x+1<=MAXR) ans += query(x+1,MAXR,1,1,MAXR);//防止区间无效
        update(x,root,1,MAXR);
    }
    printf("%lld\n",ans);
    return 0;
}

可持久化线段树(主席树)

静态区间第k小

int a[N];
 
int data[N];
void discrete(int n)//使用该函数把a[i]变成原本a[i]离散化后对应的数,data可以根据离散化后的值推实际的大小
{
    for1(i,1,n) data[i] = a[i];
    sort(data+1,data+1+n);
    int cnt = unique(data+1,data+1+n) - data;
    for1(i,1,n) a[i] = lower_bound(data+1,data+cnt,a[i]) - data;
}
 
struct node
{
    int l,r;
    int sum;
}tree[M];
int sz,root[N];
 
void push_up(int rt){
    tree[rt].sum = tree[tl].sum + tree[tr].sum;
}
 
void update(int old,int p,int& rt,int l,int r)
{
    rt = sz++;//这里容易脑抽写成if(!rt) rt = sz++
    if (l==r){
        tree[rt].sum = tree[old].sum + 1;
        return ;
    }
    tree[rt] = tree[old];
    mid;
    if (p<=m) update(tl,p,lson);
    else      update(tr,p,rson);
    push_up(rt);
}
 
int ccnt;
int query(int old,int k,int rt,int l,int r)
{
    if (l==r) return data[l];
    mid;
    ccnt = tree[tl].sum - tree[tree[old].l].sum;
    if (ccnt >= k) return query(tree[old].l,k,lson);
    else           return query(tree[old].r,k-ccnt,rson);
}
 
int main()
{
    int n,m;
    scanf("%d %d",&n,&m);
    sz = 1;
    for1(i,1,n) scanf("%d",a+i);
    discrete(n);
    for1(i,1,n) update(root[i-1],a[i],root[i],1,n);
 
    int l,r,k;
    for1(i,1,m){
        scanf("%d %d %d",&l,&r,&k);
        printf("%d\n",query(root[l-1],k,root[r],1,n));
    }
 
    return 0;
}

可持久化无旋treap

struct PBT
{
    int ch[2];
    int val;
    int cnt;
    int pri;
}t[N*50];
int root[N],sz,nowv;

inline int rnd(){
    static int seed=703;
    return seed=int(seed*48271LL%2147483647);
}

void update(int rt){
    t[rt].cnt = 1 + t[t[rt].ch[0]].cnt + t[t[rt].ch[1]].cnt;
}

int newnode(int v){
    t[sz].ch[0] = t[sz].ch[1] = 0;
    t[sz].cnt = 1;
    t[sz].pri = rnd();
    t[sz].val = v;
    return sz++;
}

int merge(int x,int y){
    if (!x || !y) return x+y;
    else {
        int now = sz++;
        if (t[x].pri < t[y].pri){
            t[now] = t[x];
            t[now].ch[1] = merge(t[now].ch[1],y);
        }
        else {
           t[now] = t[y];
          t[now].ch[0] = merge(x,t[now].ch[0]);
        }
        update(now);
        return now;
    }
}

void split(int rt,int v,int& x,int& y){
    if (!rt) x = y = 0;
    else {
        int now = sz++;
        t[now] = t[rt];
        if (t[now].val<=v){
            x = now;
            split(t[x].ch[1],v,t[x].ch[1],y);
        }
        else {
            y = now;
            split(t[y].ch[0],v,x,t[y].ch[0]);
        }
        update(now);
    }
}

void insert(int old,int v,int& rt){
    int x,y;
    split(root[old],v,x,y);
    rt = merge(merge(x,newnode(v)),y);
}
void Insert(int old,int v){insert(old,v,root[nowv++]);}

void remove(int old,int v,int& rt){
    int x,y,z;
    split(root[old],v-1,x,y);
    split(y,v,y,z);
    y = merge(t[y].ch[0],t[y].ch[1]);
    rt = merge(merge(x,y),z);
}
void Remove(int old,int v){remove(old,v,root[nowv++]);}

int rnk(int old,int v){
    root[nowv++] = root[old];
    int x,y;
    split(root[old],v-1,x,y);
    return t[x].cnt + 1 ;
}
int Rank(int old,int v){return rnk(old,v) -1;}

int kth(int rt,int k){
    if (t[t[rt].ch[0]].cnt >= k) return kth(t[rt].ch[0],k);
    k -=  t[t[rt].ch[0]].cnt+1;
    if (k<=0) return rt;
    else return kth(t[rt].ch[1],k);
}
int Kth(int old,int k){
    root[nowv++] = root[old];
    return t[kth(root[old],k+1)].val;
}

int Pre(int old,int v){
    root[nowv++] = root[old];
    int x,y;
    split(root[old],v-1,x,y);
    return t[kth(x,t[x].cnt)].val;
}

int Suc(int old,int v){
    root[nowv++] = root[old];
    int x,y;
    split(root[old],v,x,y);
    return t[kth(y,1)].val;
}

void init(){
    sz = nowv = 1;
    root[0] = 0;
    //insert(0,-2147483647,root[0]);
    //insert(0,2147483647,root[0]);
}

后缀平衡树

BZOJ没了,我也没了


树链剖分

解决树上的链,子树修改与查询(配合线段树)

const int N = 1e5+5;
int top[N];//当前点所在链的头节点
int id[N];//区间更新时实际的区间位置
int dep[N];//深度
int fa[N];//父节点
int son[N];//重儿子节点
int size[N];//子树大小
int w[N];//节点权值
int a[N];//节点初值在线段树上对应位置的初值。a[id[i]] = w[i]  

void dfs1(int now,int f,int nowdep){
    dep[now] = nowdep;
    fa[now] = f;
    size[now] = 1;
    for (int i=head[now];i!=0;i=edge[i].last){
        int v = edge[i].to;
        if (v==f) continue;
        dfs1(v,now,nowdep+1);
        size[now] += size[v];
        if (size[v] > size[son[now]]) now = v;
    }
}

int tot = 1;
void dfs2(int now,int topf){
    id[now] = tot++;
    a[id[now]] = w[now];//每个节点初始有权值,a用于更新初始权值
    top[now] = topf;
    if (!son[now]) return ;
    dfs2(son[now],topf);
    for (int i=head[now];i!=0;i=edge[i].last){
        int v = edge[i].to;
        if (v==fa[now] || v==son[now]) continue;
        dfs2(v,v);
    }
}

void interval(int x,int y){//区间修改:O(logn*logn)
    while (top[x]!=top[y]){
        if (dep[top[x]] < dep[top[y]]) swap(x,y);
        //处理区间 [id[top[x]],id[x]]
        x = fa[top[x]];
    }
    if (dep[x]

线性基

合并:我插你

交集:带着自己的成绩等人来救你吧

#define ll long long

ll lb[65];

bool insert(ll x){
    for (int i=62;i>=0;i--){
        if ((1LL<

Splay(伸展树)

#include
 
using namespace std;
 
#define ll long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;ival[now]];//第一遍说:这里是大于
        if (now) cnt[now]++;
        else {now = newnode(v,f);if (f)ch[f][v>val[f]] = now;}
        splay(now);//通过splay重构了一下树,并(主要是)正确更新了原本由于新插入一个节点而产生错误的祖先节点的信息
    }
 
    void remove(int v){
        rank(v);//找点并splay,因为还不知道v在哪,不知道从哪splay
        if (cnt[root]>1) cnt[root]--;
        else if (!ch[root][0] && !ch[root][1]) root = 0;
        else if (!ch[root][0] || !ch[root][1]) root = ch[root][0]?ch[root][0]:ch[root][1],fa[root] = 0;//fa[root] = 0很关键,不然这个点旋转时会重新搞出删掉的节点
        else {
            int p = getpre(v,true);//保证有前驱后继,因为左右子树都有才能进这个else
            int old = root;
            splay(p);//这里知道点在哪儿,直接splay ,前驱直接splay到根的话,根一定在右儿子(不会三点一线,消失不见)
            fa[ch[old][1]] = p;
            ch[p][1] = ch[old][1];
            push_up(p);
        }
    }
 
    int rank(int v){
        int now = root,ans = 0;
        while (now){
            if (val[now]>v) now = ch[now][0];
            else{
                ans += size[ch[now][0]];
                if (val[now]==v) {splay(now);return ans+1;}//此处需要splay的原因与求前驱后继有关
                ans += cnt[now];
                now = ch[now][1];
            }
        }
    }
 
    int kth(int k){
        int now = root;
        while (now){
            if (k<=size[ch[now][0]]) now = ch[now][0];
            else {
                k -= size[ch[now][0]] + cnt[now];
                if (k<=0) return val[now];
                else now = ch[now][1];
            }
        }
    }
 
    int getpre(int v,bool getid=false){//getid = true就返回前驱的下标
        int ans = -INF,id = 0;
        int now = root;
        while (now){
            if (val[now]>=v) now = ch[now][0];
            else{
                if (val[now]>ans) ans = val[now],id = now;
                now = ch[now][1];
            }
        }
        return getid?id:ans;
    }
 
    int getsuc(int v,bool getid=false){
        int ans = INF,id = 0;
        int now = root;
        while (now){
            if (val[now]<=v) now = ch[now][1];
            else {
                if (val[now]

 


随机生成一棵树 + 随机生成两个节点询问路径第k小(对拍用)

//不保证一定没有问题

#include

using namespace std;

#define ll long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i=0;i--){
        if (lca[x][i]!=lca[y][i]) x = lca[x][i],y = lca[y][i];
    }
    return lca[x][0];
}
//**************************************************************************************
int main()
{
    srand(time(0));
    freopen("C:/Users/DELL/Desktop/input.txt", "w", stdout);//输入想要保存文件的路径
    /*u*/ //修改数据组数
    int T = 100;//数据组数,可修改
    printf("%d\n",T);
    /*d*/
    /*u*/ //修改生成的树最多节点个数,必须<=1e5
    int maxsize = 20;
    /*d*/
    while(T--){
        /*u*/ //修改当前这组数据的节点数以及询问次数,q默认10次
        int n = Rand()%(maxsize+1),q=10;
        if (!n) n++;
        /*d*/
        printf("%d %d\n",n,q);

        for1(i,1,n) uf[i] = i,head[i] = 0;
        memset(lca,0,sizeof lca);
        id = 1;

        for1(i,1,n){
            if (i!=1) printf(" ");
            printf("%d",Rand(true));
        }puts("");//生成每个节点的值

        int hulue = Rand()%(n+1);
        if (!hulue) hulue++;
        for1(i,1,n){
            if (i==hulue) continue;
            int v;
            do {
                v = Rand()%(n+1);
                if (!v) v++;
            }while (find1(i)==find1(v));
            join(i,v);//并查集
            printf("%d %d\n",i,v);//生成数据
            add(i,v);add(v,i);//生成树,用于check合法的k
        }//随机生成一棵树结束

        dfs(1,0,1);
        getst(n);

        while(q--){
            int u = Rand()%(n+1),v = Rand()%(n+1);
            if (!u) u++;if (!v) v++;
            int LCA = getlca(u,v);
            int maxk = dep[u]-dep[LCA]+dep[v]-dep[LCA]+1;
            int k = Rand()%(maxk+1);
            if (!k) k++;
            printf("%d %d %d\n",u,v,k);
        }
    }
    return 0;
}

FFT+NTT的准备工作

struct cp{
    double x,y;
    cp(double xx=0,double yy=0){x=xx;y=yy;}
    double real(){return x;}
    double imag(){return y;}
    cp friend operator + (cp a,cp b){return cp(a.x+b.x,a.y+b.y);}
    cp friend operator - (cp a,cp b){return cp(a.x-b.x,a.y-b.y);}
    cp friend operator * (cp a,cp b){return cp(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
};
int rev[N];
 
void getrev(int n){//获取下标二进制下翻转后对应的位置的数组0~n-1
    int bit = 1;
    while ( (1<>1]>>1) + ((1&i)<>=1;
        a = a*a%mod;
    }
    return ans;
}
 
ll getny(ll a){
    return ft(a,mod-2);
}

FFT

void FFT(cp a[],int RB,int inv){
    for0(i,0,RB){
        if (i>1;
        cp wn = cp(cos(2*pi*1/n),inv*sin(2*pi*1/n));//总共只需调用logn次三角函数,这里是TLE的关键
        for (int st = 0;st < RB;st += n){
            cp wnk = cp(1,0);
            for0(i,st+0,st+mid){
                cp x = wnk*a[i+mid];
                a[i+mid] = a[i] - x;
                a[i] = a[i] + x;
                wnk = wnk*wn;//模长相乘 极角相加的运用
            }
        }
    }
    if (inv==-1){
        for0(i,0,RB) a[i].x = (int)(a[i].real()/RB + 0.5);
    }
}

NTT

void NTT(ll a[],int RB,int inv){
    for0(i,0,RB) if (i (mod-1)* 1/n
        if (inv==-1) wn = getny(wn);
        for (int st = 0;st < RB;st += n){
            ll wnk = 1;//cp(1,0) --> 1
            for0(i,st+0,st+mid){
                int x = wnk*a[i+mid]%mod;
                a[i+mid] = (a[i] - x + mod)%mod;//减法记得加个模
                a[i] = (a[i] + x)%mod;
                wnk = wnk*wn%mod;
            }
        } 
    }
    if (inv==-1) {
        ll rbny = getny(RB);
        for0(i,0,RB) a[i] = a[i]*rbny%mod;//注意这里不是/RB了
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(模板)