整理的小技巧

一种很有意思的GCD写法(代码为xuziyang大佬所写)

long long gcd(long long a,long long b){
    if(a==0)
        return b;
    if(b==0)
        return a;
    if(!(a&1)&&!(b&1))//判断“如果a、b都为偶数”。&1表示按位与二进制的00……001,还因为奇数的二进制为1,偶数的二进制为0,所以奇数它就会返回1,偶数就返回0;再取个!非符号,偶数就变成1
        return 2*gcd(a>>1,b>>1);//a/2等价于a>>1
    else if(!(a&1))//判断是不是只有a为偶数
        return gcd(a>>1,b);
    else if(!(b&1))//判断是不是只有b为偶数
        return gcd(a,b>>1);
    else//剩下的情况就是两个都为奇数
        return gcd(abs(a-b),min(a,b));
}

  

johnson算法

 

#include<cstdio>
#include<queue>
#define MAXN 5005
#define MAXM 10005
#define INF 1e9
using namespace std;
int n,m;
int vis[MAXN];
long long h[MAXN],dis[MAXN];
bool f[MAXN];
struct graph
{
    int tot;
    int hd[MAXN];
    int nxt[MAXM],to[MAXM],dt[MAXM];
    void add(int x,int y,int w)
    {
        tot++;
        nxt[tot]=hd[x];
        hd[x]=tot;
        to[tot]=y;
        dt[tot]=w;
        return ;
    }
}g;//链式前向星
bool SPFA(int s)//这里用了Bellman-Ford的队列优化
{
    queue<int>q;
    for(int i=1;i<=n;++i) h[i]=INF,f[i]=false;
    h[s]=0;
    f[s]=true;
    q.push(s);
    while(!q.empty())
    {
        int xx=q.front();
        q.pop();
        f[xx]=false;
        for(int i=g.hd[xx];i;i=g.nxt[i])
            if(h[g.to[i]]>h[xx]+g.dt[i])
            {
                h[g.to[i]]=h[xx]+g.dt[i];
                if(!f[g.to[i]])
                {
                    if(++vis[g.to[i]]>=n) return false;//注意在有重边的情况下要记录入队次数而不是松弛次数
                    f[g.to[i]]=true,q.push(g.to[i]);
                }
            }
    }
    return true;
}
void dijkstra(int s)
{
    priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
    for(int i=1;i<=n;i++) dis[i]=INF,f[i]=false;
    q.push(make_pair(0,s));
    dis[s]=0;
    while(!q.empty())
    {
        int xx=q.top().second;
        q.pop();
        if(!f[xx])
        {
            f[xx]=true;
            for(int i=g.hd[xx];i;i=g.nxt[i])
                if(dis[g.to[i]]>dis[xx]+g.dt[i])
                {
                    dis[g.to[i]]=dis[xx]+g.dt[i];
                    if(!f[g.to[i]])
                        q.push(make_pair(dis[g.to[i]],g.to[i]));
                }
        }
    }
    return ;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        g.add(u,v,w);
    }
    for(int i=1;i<=n;i++) g.add(0,i,0);//建虚拟节点0并且往其他的点都连一条边权为0的边
    if(!SPFA(0))//求h的同时也判了负环
    {
        printf("-1");
        return 0;
    }
    for(int u=1;u<=n;u++)
        for(int i=g.hd[u];i;i=g.nxt[i])
            g.dt[i]+=h[u]-h[g.to[i]];//求新边的边权
    for(int i=1;i<=n;i++)
    {
        dijkstra(i);//以每个点为源点做一遍dijkstra
        long long ans=0;
        for(int j=1;j<=n;j++)//记录答案
            if(dis[j]==INF) ans+=1ll*j*INF;
            else ans+=1ll*j*(dis[j]+(h[j]-h[i]));
        printf("%lld\n",ans);
    }
    return 0;
}

并查集两个优化同时使用

 

#include<stdio.h>
#include<stdlib.h>
#define MAXN 5005
int fa[MAXN];
int rank[MAXN];
void init(int n)
{
    for (int i = 0; i < n; ++i)
    {
        fa[i] = i;
        rank[i] = 1;

    }
}
int f_root(int x) 
{
    if (fa[x] == x)
        return x;
    else {
        fa[x]=f_root(fa[x]);
        return fa[x];
    }
}
void merge(int i, int j)
{
    int x = f_root(i), y = f_root(j);
    if (rank[x] <= rank[y])
    {   
        fa[x] = y;
    }
    else 
        fa[y] = x;
        if (rank[x] == rank[y]&& x!=y)
        {
            rank[y]++;
        }
}
int main()
{
    int n, m, p, x, y;
    scanf("%d%d%d", &n, &m, &p);
    init(n);
    for (int i = 0; i < m ;++i)
    {
        scanf("%d%d", &x, &y);
        merge(x, y);
    }
    for (int  i = 0; i < p; ++i)
    {
        scanf("%d%d", &x, &y);
        printf("%s\n", f_root(x) == f_root(y) ? "Yes":"No");
    }
    return 0;
}

点分治模板

#include <cstdio>
#include <algorithm>
#define Max 20010
#define add(u,v,w) (To[++num]=Head[u],Head[u]=num,V[num]=v,W[num]=w)
#define For(x) for (int h=Head[x],o=V[h]; h; h=To[h],o=V[h])
#define Input for (int i=1,u,v,w; i<N; i++) scanf("%d%d%d",&u,&v,&w),add(u,v,w),add(v,u,w)
using namespace std;
int N,K,root,num,ans,cnt,mins,Head[Max],To[Max],V[Max],W[Max],siz[Max],dis[Max],f[Max];
/*
    程序中部分变量说明
    dis[i]  所有路径到 重心 的长度
    siz[i]  以 i 为根节点的子树的大小(当前重心下)
    f[i]    i 是否还存在(分治完一次后就把重心删掉了)
    cnt     记录 dis 的个数(即路径个数)
    root    当前子树的重心
    maxs    当前讨论的点所有子树大小中最大值(并不是全局变量,是尝试每个重心时重新开的一个变量)
    mins    讨论过的点的子树大小中最大的最小值(是全局变量,用来确定哪个才是重心)
*/

int get_size(int x,int fa){     //返回以 x 为根的子树大小,其中 x 父节点为 fa 
    siz[x]=1;
    for(int i=Head[x];i;i=To[i]){
        int vt=V[i];
        if(vt==fa){
            continue;
        }
        if(f[vt]){
            continue;
        }
        siz[x]+=gs(vt,x);
    }
    return siz[x];
}

void get_dis(int x,int d,int fa){   //x 到重心的长度为 d,之后继续 dfs 
    dis[++cnt]=d;
    For(x) if (o!=fa && !f[o])
        get_dis(o,d+W[h],x);
    return;
}

void dfs_root(int x,int tot,int fa) {
    //求目标子树的重心(要求除去 x 点时,它的 maxs 值最小,那么 x 就是这棵子树的重心了),其中 tot 是这棵子树的总大小(节点个数) 
    int maxs=tot-siz[x];    //这棵子树中x 父亲那一支先赋给 maxs 
    For(x) if (o!=fa && !f[o]){
        maxs=max(maxs,siz[o]);
        dfs_root(o,tot,x);
    }
    if (maxs<mins){
        mins=maxs;
        root=x;
    }
    return;
}

int work(int x,int d) {
    //返回以 x 为根的子树内长度小于等于 K 的路径数(两个端点都在子树内) 
    //其实 d 在这里用处只有一个,是在做减法时方便把重心的儿子节点的 dis 先弄好,你也可以在分治的时候弄,不过就稍微有点麻烦了 
    cnt=0;
    get_dis(x,d,0);
    sort(dis+1,dis+cnt+1);
    int daan=0,i=1,j=cnt; 
    while (i<j){
        while (i<j && dis[i]+dis[j]>K) j--;
        daan+=j-i;  //相当于选一条路径 i,另一条可以为 [i+1,j] 里任意一条路径,这样得到的两个点之间长度(经过重心的那条路径)肯定是小于等于 K 的 
        i++;
    }
    return daan;
}

void dfs(int x){    //以 x 为重心分治一下 
    cnt=0;
    mins=Max;
    get_size(x,0);
    dfs_root(x,siz[x],0);
    ans+=work(root,0);
    f[root]=1;
    For(root) if (!f[o]){       //注意这里是以重心开始 
        ans-=work(o,W[h]);      //注意,这里 dis[o] 要先赋成 W[h](即它到重心的距离) 
        dfs(o);
    }
    return;
}

int main(){
    while(scanf("%d%d",&N,&K)!=EOF && N && K){
        Input;
        dfs(1);
        printf("%d\n",ans);

        num=ans=0;
        for (int i=1; i<=N; i++) Head[i]=f[i]=dis[i]=0;
    }
    return 0;
}

后缀数组模板标程+资料

#include<bits/stdc++.h>
#define N 1000050
using namespace std;
char s[N]; int rank[N],y[N],tmp[N]; 
int c[N],SA[N],n,m,Height[N];
void Sort(){
    for(int i=1;i<=m;i++) c[i] = 0;
    for(int i=1;i<=n;i++) c[rank[i]]++;
    for(int i=2;i<=m;i++) c[i] += c[i-1];
    for(int i=n;i>=1;i--) SA[c[rank[y[i]]]--] = y[i];
}
void get_SA(){
    for(int i=1;i<=n;i++) rank[i] = s[i] , y[i] = i;
    Sort();
    for(int k=1;k<=n;k<<=1){
        int cnt = 0;
        for(int i=n-k+1;i<=n;i++) y[++cnt] = i;
        for(int i=1;i<=n;i++) if(SA[i]>k) y[++cnt] = SA[i] - k;
        Sort(); swap(rank,tmp); int num = 1; rank[SA[1]] = 1;
        for(int i=2;i<=n;i++){
            if(tmp[SA[i]] == tmp[SA[i-1]] && tmp[SA[i]+k] == tmp[SA[i-1]+k])
                rank[SA[i]] = num;
            else rank[SA[i]] = ++num;
        } m = num;
    }
}
void get_Hi(){
    int k = 0;
    for(int i=1;i<=n;i++){
        if(rank[i]==1) continue;
        int j = SA[rank[i]-1]; if(k) k--;
        while(i+k<=n && j+k<=n && s[i+k]==s[j+k]) k++; 
        Height[rank[i]] = k;
    }
} 
int main(){
    scanf("%s",s+1); n = strlen(s+1);
    m = 127; get_SA();  get_Hi();
    for(int i=1;i<=n;i++) printf("%d ",SA[i]);
    return 0;
} 
//正常代码

后缀数组简介 - OI Wiki (oi-wiki.org)

posted @ 2022-03-03 20:35  21xf2257  阅读(27)  评论(0编辑  收藏  举报