ACM板子

文章目录

  • 板子:
      • 初始化:
      • 快读:
      • 快速幂:
      • GCD/LCM:
      • 组合数:
      • 欧拉筛:
      • 大整数质因数分解:
      • 分解质因数:
      • 求 (1e12) 内质数:
      • KMP:
      • 最小生成树:
      • 最短路
      • LCA查找最近祖先
      • 二分图匹配
      • RMQ区间最小值:
      • 01字典树:
      • 字典树:
      • 线段树:
      • 最长上升子序列:
      • 最长公共子序列:
      • 01背包
      • 中国剩余定理模板
      • *L**u**c**a**s*定理。
      • 扩展Lucas定理
      • hash+二分 求最长回文串
      • **尼姆博弈模型**
      • 莫队算法
      • 权值线段树
      • 回文树


板子:

int run(int year) {  if((year%4==0&&year%100!=0)||(year%400==0)) 	return 1; else 	return 0; }

**蓝桥杯函数:**

全排列函数:next_permutation(a.begin(), a.end())

stoi(s) 将字符串s转为数字

二分查找:binary_search(arr[],arr[]+size , indx)


__builtin_popcount(n) :n的⼆进制中1的个数
__builtin_ffs(n):返回最低的非 0 位的下标,下标从 1 还是计数,0未定义
__builtin_clz(n):返回从最高位开始连续的 0 的个数
__builtin_ctz(n):返回从最低位开始的连续的 0 的个数;如果传入 0 则行为未定义
    

1.判断一个数字x二进制下第i位是不是等于1。(最低第1位)

方法:if(((1<<(i−1))&x)>0) 将1左移i-1位,相当于制造了一个只有第i位 上是1,其他位上都是0的二进制数。然后与x做与运算,如果结果>0, 说明x第i位上是1,反之则是0。

2.将一个数字x二进制下第i位更改成1。

方法:x=x|(1<<(i−1)) 证明方法与1类似。

3.将一个数字x二进制下第i位更改成0。

方法:x=x&~(1<<(i−1))

4.把一个数字二进制下最靠右的第一个1去掉。

方法:x=x&(x−1)
    
    按位或运算符(|)

运算规则:参加运算的两个数只要两个数中的一个为1,结果就为1。

即 0 | 0= 0 , 1 | 0= 1 , 0 | 1= 1 , 1 | 1= 1 。

异或运算符(^)

运算规则:参加运算的两个数,如果两个相应位为“异”(值不同),则该位结果为1,否则为0。

即 0 ^ 0=0 , 0 ^ 1= 1 , 1 ^ 0= 1 , 1 ^ 1= 0 。

按位与运算符(&)

运算规则:只有两个数的二进制同时为1,结果才为1,否则为0。(负数按补码形式参加按位与运算)

即 0 & 0= 0 ,0 & 1= 0,1 & 0= 0, 1 & 1= 1。

fill()函数参数:fill(first,last,val); 、//可以相当于memset赋值 ,赋值随意。
ceil() 函数向上舍入为最接近的整数。
**提示**:如需向下舍入为最接近的整数,请查看 floor() 函数。
**提示**:如需对浮点数进行四舍五入,请查看 round() 函数。

初始化:

#pragma GCC optimize(2)
#include 
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define CAS int cas;cin>>cas;while(cas--)
#define  mem(a,b) memset(a,b,sizeof(a))
#define f(i,l,h) for(int i=l;i<=h;++i)
#define eps 1e-9
#define pi acos(-1.0)
#define pb push_back
#define se second
#define fi first
const int maxn=500005;
const int INF = 0x3f3f3f3f;
typedef pair  pii;
typedef long long  ll;

using namespace std;

快读:

ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
inline void Prin(ll x){if(x < 0){putchar('-');x = -x;}if(x > 9) Prin(x / 10);putchar(x % 10 + '0');}
usin

inline int read(){//快读,可快了
	int x=0,f=0;char ch=getchar();
	while(!isdigit(ch))f|=ch=='-',ch=getchar();
	while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
	return f?-x:x;
}

快速幂:

ll quick(ll a,ll b,ll m) {
       ll ans=1;
      while(b) {
           if(b&1)
            ans=(a*ans)%m;
          a=(a*a)%m;
          b>>=1;
    }
 return ans;
}

GCD/LCM:

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

ll lcm(ll a, ll b) {
    return a / gcd(a, b) * b;
}

组合数:

#define mod 1000000007
ll fac[maxn],inv[maxn];

void init(){
	fac[0] = inv[0] = 1;
	fac[1] = inv[1] = 1;
	for(int i = 2; i <= maxn - 9; ++i)// 一定要从2开始
		fac[i] = fac[i-1] * i % mod,
		inv[i] = (mod - mod / i) * inv[mod % i] % mod;
	for(int i = 1; i <= maxn - 9; ++i)
		inv[i] = inv[i] * inv[i-1] % mod;
}


ll c(int n,int m){
    if(m>n||m<0) return 0;
        return fac[n]*inv[m]%mod*inv[n-m]%mod;
}

欧拉筛:

const int maxx = 1e6+10;
int prime[maxx+10];
bool vis[maxx+10];
int k=0;

void init(){
    vis[0]=vis[1]=1;
    for(int i=2;i<=maxx;i++){
        if(vis[i]==0)prime[++k]=i,vis[i]=1;
        for(int j=1;i*prime[j]<=maxx&&j<=k;j++){
            vis[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
}

大整数质因数分解:

const int MAXN = 10000005 ;
#define int64_t long long
int64_t mulEx(int64_t a , int64_t b , int64_t Mod) {///logn快速乘
    if(!a) return 0 ;
    int64_t ans(0) ;
    while(b)
    {
		if(b & 1) ans = (ans + a) % Mod;
		a <<= 1 ;
		a %= Mod ;
		b >>= 1 ;
    }
    return ans ;
}

int64_t powEx(int64_t base , int64_t n , int64_t Mod)
{///快速幂
    int64_t ans(1) ;
    while(n)
    {
        if(n & 1) ans = mulEx(ans , base , Mod) ;
        base = mulEx(base , base , Mod) ;
        n >>= 1 ;
    }
    return ans ;
}

bool check(int64_t a , int64_t d , int64_t n)
{
    if(n == a) return true ;
    while(~d & 1) d >>= 1 ;
    int64_t t = powEx(a , d , n) ;
    while(d < n - 1 && t != 1 && t != n - 1)
    {
        t = mulEx(t , t , n) ;
        d <<= 1 ;
    }
    return (d & 1) || t == n - 1 ;
}

bool isP(int64_t n)
{ ///判断大数是否是质数
    if(n == 2) return true ;
    if(n < 2 || 0 == (n & 1)) return false ;
    static int p[5] = {2 , 3 , 7 , 61 , 24251} ;
    for(int i = 0 ; i < 5 ; ++ i) if(!check(p[i] , n - 1 , n)) return false ;
    return true ;
}

int64_t gcd(int64_t a , int64_t b)
{
    if(a < 0) return gcd(-a , b) ;
    return b ? gcd(b , a - b * (a / b)) : a ;
}

int64_t Pollard_rho(int64_t n , int64_t c)
{///大数分解质因数
    int64_t i = 1 , k = 2 , x = rand() % n , y = x ;
    while(true)
    {
        x = (mulEx(x , x , n) + c) % n ;
        int64_t d = gcd(y - x , n) ;
        if(d != 1 && d != n) return d ;
        if(y == x) return n ;
        if(++ i == k)
        {
            y = x ;
            k <<= 1 ;
        }
    }
}

vectorFac, factCnt ;
///Fac存的是质因子,大小不一定按照顺序,有重复
void factorization(int64_t n)
{
    if(n==1) return ;
    if(isP(n))
    {
        Fac.push_back(n) ;
        return ;
    }
    int64_t p(n) ;
    while(p >= n) p = Pollard_rho(p , rand() % (n - 1) + 1) ;
    factorization(p) ;
    factorization(n / p) ;
}

map factMap ;
///遍历map的first表示因子,second表示次数

void getFactor(int64_t x)
{///不用判断是否是质数,但是比较费时间
 /**因此最好先判断一下是否是质数**/
	srand(time(0)) ;
	//factCnt = 0 ;
	factMap.clear() ;
	factorization(x) ;
	for(int i = 0; i < Fac.size(); ++i) ++ factMap[Fac[i]] ;
}

分解质因数:

//求每个数最小质因子
int cnt=0,lim=1e6+5;

void getprime(){
    int cnt=0;
    isprime[1]=1;
    for(int i=2;i<=lim;i++){
        if(isprime[i]==0){
            prime[++cnt]=i,isprime[i]=i;
        }
        for(int j=1;j<=cnt&&i*prime[j]<=lim;j++){
            isprime[i*prime[j]]=prime[j];
            if(i%prime[j]==0){
                break;
            }
        }
    }
}

//分解质因数
 while(x!=1) {
     ++pz[isprime[x]];
     x/=isprime[x];
 }

求 (1e12) 内质数:

#include 
using namespace std;
using ll = long long;
//通过知道前面的 n^1/3 的质数可以推断后面n^2/3的质数所以可以适当减小
const int N  = 9e3;
const int M  = 2;         //为了减小内存可以不过是质数
const int PM = 2 * 3 * 5; //为了减小内存可以不过要按质数减小如去掉17
ll n;
bool np[N];
int prime[N], pi[N];
int phi[PM + 1][M + 1], sz[M + 1];

int getprime() {
    int cnt = 0;
    np[0] = np[1] = true;
    pi[0] = pi[1] = 0;
    for (int i = 2; i < N; ++i) {
        if (!np[i]) prime[++cnt] = i;
        pi[i] = cnt;
        for (int j = 1; j <= cnt && i * prime[j] < N; ++j) {
            np[i * prime[j]] = true;
            if (i % prime[j] == 0) break;
        }
    }
    return cnt;
}

void init() {
    getprime();
    sz[0] = 1;
    for (int i = 0; i <= PM; ++i) phi[i][0] = i;
    for (int i = 1; i <= M; ++i) {
        sz[i] = prime[i] * sz[i - 1];
        for (int j = 1; j <= PM; ++j) phi[j][i] = phi[j][i - 1] - phi[j / prime[i]][i - 1];
    }
}

int sqrt2(ll x) {
    ll r = (ll)sqrt(x - 0.1);
    while (r * r <= x) ++r;
    return int(r - 1);
}

int sqrt3(ll x) {
    ll r = (ll)cbrt(x - 0.1);
    while (r * r * r <= x) ++r;
    return int(r - 1);
}

ll getphi(ll x, int s) {
    if (s == 0) return x;
    if (s <= M) return phi[x % sz[s]][s] + (x / sz[s]) * phi[sz[s]][s];
    if (x <= prime[s] * prime[s]) return pi[x] - s + 1;
    if (x <= prime[s] * prime[s] * prime[s] && x < N) {
        int s2x = pi[sqrt2(x)];
        ll ans  = pi[x] - (s2x + s - 2) * (s2x - s + 1) / 2;
        for (int i = s + 1; i <= s2x; ++i) ans += pi[x / prime[i]];
        return ans;
    }
    return getphi(x, s - 1) - getphi(x / prime[s], s - 1);
}

ll getpi(ll x) {
    if (x < N) return pi[x];
    ll ans = getphi(x, pi[sqrt3(x)]) + pi[sqrt3(x)] - 1;
    for (int i = pi[sqrt3(x)] + 1, ed = pi[sqrt2(x)]; i <= ed; ++i) ans -= getpi(x / prime[i]) - i + 1;
    return ans;
}

ll lehmer_pi(ll x) { //小于等于n的素数有多少个
    if (x < N) return pi[x];
    int a  = (int)lehmer_pi(sqrt2(sqrt2(x)));
    int b  = (int)lehmer_pi(sqrt2(x));
    int c  = (int)lehmer_pi(sqrt3(x));
    ll sum = getphi(x, a) + (ll)(b + a - 2) * (b - a + 1) / 2;
    for (int i = a + 1; i <= b; i++) {
        ll w = x / prime[i];
        sum -= lehmer_pi(w);
        if (i > c) continue;
        ll lim = lehmer_pi(sqrt2(w));
        for (int j = i; j <= lim; j++) sum -= lehmer_pi(w / prime[j]) - (j - 1);
    }
    return sum;
}

int main() {
    ios_base::sync_with_stdio(false), cin.tie(0);
    init();
    while (cin >> n && n) cout << lehmer_pi(n) << "\n";
    return 0;
}

KMP:

int nex[N];

void getnex(string s){
    int k=-1;
    int i=0;
    nex[0]=-1;
    int len=s.size();
    while(i=lenp) return i-lenp;
    else return -1;
}

最小生成树:

#include
#include
#include
#define N 100010
using namespace std;
int n, m, ans = 0, cnt = 0;
int head[N], fa[N];
struct edge{
	int u, v, w;
}e[N << 1];
inline int read(){
	int s = 0;
	char ch = getchar();
	while(ch < '0' || ch > '9') ch = getchar();
	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
	return s;
}
bool cmp(edge a, edge b){return a.w < b.w;}
int find(int x){	//!!!非常重要,并查集模板 
	if(x != fa[x]) fa[x] = find(fa[x]);
	return fa[x];
}
void Kruskal(){	//Kruskal模板 
	sort(e + 1, e + m + 1, cmp);
	for(int i = 1; i <= n; i++) fa[i] = i;
	for(int i = 1; i <= m; i++){
		int x = find(e[i].u), y = find(e[i].v);
		if(x == y) continue;	//如果两条边已经联通那么跳过 
		ans += e[i].w;
		fa[x] = y;	//将两个点合并 
		if(++cnt == n - 1) break; //当n - 1条边时结束 
	}
}
int main(){
	n = read(); m = read();
	for(int i = 1; i <= m; i++){
		int x, y, z; 
		x = read(); y = read(); z = read();
		e[i].u = x; e[i].v = y; e[i].w = z;
	}
	Kruskal();
	printf("%d\n", ans);
	return 0;
}

最短路

//Dijkstra算法
#define ll long long
const int N=200005;
const int inf=0x3f3f3f3f;
struct node{
    int v;
    int c;
    bool operator <(const node &r)
     const{
         return c>r.c;
     }
};
struct edge{
    int v,val;
};
vector g[N];
int vis[N];
ll dis[N];

void dij(int n,int st){
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++) dis[i]=inf;
    priority_queue q;
    while(!q.empty())
        q.pop();
    dis[st]=0;
    q.push({st,0});
    node tmp;
    while(!q.empty()){
        tmp=q.top();
        q.pop();
        int u=tmp.v;
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=0;idis[u]+val){
                dis[v]=dis[u]+val;
                q.push({v,dis[v]});
            }
        }
    }
}
 


//spfa算法

const int N=100005;
struct node{
    int to,val;
}e;

vector g[N];

int vis[N],dis[N];
int n,m,st,ed;

queueq;

void spfa(int x){
     for(int i=1;i<=n;i++) dis[i]=inf;
     dis[x]=0;
     q.push(x);
     vis[x]=1;
     cnt[st]=1;
     while(!q.empty()){
        int u=q.front();q.pop();vis[u]=0;
        for(int i=0;idis[u]+w){
                dis[v]=dis[u]+w;
                if(!vis[v]){
                    vis[v]=1,q.push(v);
                    if(++cnt[v]>n)return false;  //cnt[i] 为入队列次数,用来判定是否存在负环回路
                }
            }
        }
     }

}


LCA查找最近祖先

//Tarjan算法
Tarjan(u,fa)//marge和find为并查集合并函数和查找函数
{
    for each(u,v)    //访问所有u子节点v
    {
        if(v==fa||vis[v]) continue;
        Tarjan(v,u);        //继续往下遍历
        marge(u,v);    //合并v到u上
        标记v被访问过;
    }
    for each(u,e)    //访问所有和u有询问关系的e
    {
        如果e被访问过;
        u,e的最近公共祖先为find(e);
    }
}
//倍增 ST
int d[N],p[N][22],w[N][22]; //w[i][j]: i为起点 到2^j内 单边最大权值
/*
void dfs(int u,int f){
    for(int i=0;id[b]) swap(a,b);
    for(int i=20;i>=0;i--){
        if(d[a]<=d[b]-(1<=0;i--){
        if(p[a][i]==p[b][i]) continue;
        else{
            a=p[a][i]; b=p[b][i];
        }
    }
    return p[a][0];
}


二分图匹配

#include 
#include 
#include 
using namespace std;
const int N = 205;

vectorG[N];
int n,m;
int vis[N],pre[N]; //标记  存男朋友
int c[N]; //颜色

//用染色法判断这个图是否是二分图
bool Dfs(int u,int colour){
    c[u] = colour;
    for(int i = 0;i < G[u].size();i++){
        int v = G[u][i];
        if(c[v] == 0 && Dfs(v,3-colour)){
            return true;
        }else if(c[v] != 3-colour) {
            printf("No\n");
            return true;
        }
    }
    return false;
}
//用匈牙利算法求出最大匹配数
bool dfs(int u){
    for(int i = 0;i 

RMQ区间最小值:

void rmq_init(){
    for(int i=1;i<=N;i++)
        dp[i][0]=arr[i];//初始化
    for(int j=1;(1<

01字典树:

const int MAXN = 5e5 + 5;
int nxt[MAXN*32][2], cnt;
int MAXBIT = 31,val[MAXN];
void init(){
    nxt[0][0] = nxt[0][1] = 0;
    cnt = 1;
}
void add(int n){
    int cur = 0;
    for (int i = MAXBIT; i >= 0; --i) {
        int bit = (n >> i) & 1;       
        if (!nxt[cur][bit]) {
            nxt[cnt][0] = nxt[cnt][1] = 0;
            nxt[cur][bit] = cnt++;
        }
        cur = nxt[cur][bit];
    }
    val[cur] = n;
}
int que_mx(int x) {
    int u = 0;
    for (int i = MAXBIT; i >= 0; i--) {
        int bit = ((x >> i) & 1);
        if (nxt[u][bit ^ 1]) u = nxt[u][bit ^ 1];
        else u = nxt[u][bit];
    }
    return val[u]^x;
}
int que_mi(int x) {
    int u = 0;
    for (int i = MAXBIT; i >= 0; i--) {
        int bit = ((x >> i) & 1);
        if (nxt[u][bit]) u = nxt[u][bit];
        else u = nxt[u][bit ^ 1];
    }
    return val[u]^x;
}


字典树:

int trie[N][26];
int cnt[N];
int id;

void insert(string s)
{
	int p = 0;
	for (int i = 0; i < s.size(); i++)
	{
		int x = s[i] - 'a';
		if (trie[p][x] == 0) trie[p][x] = ++id;
		p = trie[p][x];
	}
	cnt[p]++;
}

int  find(string s)
{
	int p = 0;
	for (int i = 0; i < s.size(); i++)
	{
		int x = s[i] - 'a';
		if (trie[p][x] == 0)return 0;
		p = trie[p][x];
	}
	return cnt[p];
}

线段树:

//定义
#define maxn 100007  //元素总个数
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
int Sum[maxn<<2],Add[maxn<<2];//Sum求和,Add为懒惰标记 
int A[maxn],n;//存原数组数据下标[1,n] 

/*建树*/

//PushUp函数更新节点信息 ,这里是求和
void PushUp(int rt){Sum[rt]=Sum[rt<<1]+Sum[rt<<1|1];}

//Build函数建树 
void Build(int l,int r,int rt){ //l,r表示当前节点区间,rt表示当前节点编号
	if(l==r) {//若到达叶节点 
		Sum[rt]=A[l];//储存数组值 
		return;
	}
	int m=(l+r)>>1;
	//左右递归 
	Build(l,m,rt<<1);
	Build(m+1,r,rt<<1|1);
	//更新信息 
	PushUp(rt);
}

/*点修改*/

void Update(int L,int C,int l,int r,int rt){//l,r表示当前节点区间,rt表示当前节点编号
	if(l==r){//到叶节点,修改 
		Sum[rt]+=C;
		return;
	}
	int m=(l+r)>>1;
	//根据条件判断往左子树调用还是往右 
	if(L <= m) Update(L,C,l,m,rt<<1);
	else       Update(L,C,m+1,r,rt<<1|1);
	PushUp(rt);//子节点更新了,所以本节点也需要更新信息 
} 

/*区间修改*/
void Update(int L,int R,int C,int l,int r,int rt){//L,R表示操作区间,l,r表示当前节点区间,rt表示当前节点编号 
	if(L <= l && r <= R){//如果本区间完全在操作区间[L,R]以内 
		Sum[rt]+=C*(r-l+1);//更新数字和,向上保持正确
		Add[rt]+=C;//增加Add标记,表示本区间的Sum正确,子区间的Sum仍需要根据Add的值来调整
		return ; 
	}
	int m=(l+r)>>1;
	PushDown(rt,m-l+1,r-m);//下推标记
	//这里判断左右子树跟[L,R]有无交集,有交集才递归 
	if(L <= m) Update(L,R,C,l,m,rt<<1);
	if(R >  m) Update(L,R,C,m+1,r,rt<<1|1); 
	PushUp(rt);//更新本节点信息 
} 


/*区间查询*/

//下推标记
void PushDown(int rt,int ln,int rn){
	//ln,rn为左子树,右子树的数字数量。 
	if(Add[rt]){
		//下推标记 
		Add[rt<<1]+=Add[rt];
		Add[rt<<1|1]+=Add[rt];
		//修改子节点的Sum使之与对应的Add相对应 
		Sum[rt<<1]+=Add[rt]*ln;
		Sum[rt<<1|1]+=Add[rt]*rn;
		//清除本节点标记 
		Add[rt]=0;
	}
}

int Query(int L,int R,int l,int r,int rt){//L,R表示操作区间,l,r表示当前节点区间,rt表示当前节点编号
	if(L <= l && r <= R){
		//在区间内,直接返回 
		return Sum[rt];
	}
	int m=(l+r)>>1;
	//下推标记,否则Sum可能不正确
	PushDown(rt,m-l+1,r-m); 
	
	//累计答案
	int ANS=0;
	if(L <= m) ANS+=Query(L,R,l,m,rt<<1);
	if(R >  m) ANS+=Query(L,R,m+1,r,rt<<1|1);
	return ANS;
} 


最长上升子序列:

int a[MAXX],dp[MAXX]; // a数组为数据,dp[i]表示长度为i+1的LIS结尾元素的最小值

int main()
{
    int n;
    while(cin>>n)
    {
        for(int i=0; i>a[i];
            dp[i]=INF; // 初始化为无限大
        }
        int pos=0;    // 记录dp当前最后一位的下标
        dp[0]=a[0];   // dp[0]值显然为a[0]
        for(int i=1; idp[pos])    // 若a[i]大于dp数组最大值,则直接添加
                dp[++pos] = a[i];
            else    // 否则找到dp中第一个大于等于a[i]的位置,用a[i]替换之。
                dp[lower_bound(dp,dp+pos+1,a[i])-dp]=a[i];  // 二分查找
        }
        cout<

最长公共子序列:

for(int i=1;i<=n;i++) cin>>a[i],mp[a[i]]=i;
    for(int j=1;j<=n;j++) cin>>b[j];
    int tot=0;dp[0]=mp[b[1]];
    for(int i=2;i<=n;i++){
        if(dp[tot]

01背包

for i=1..N
  for j=V..v[i]//不用到0,因为若小于v[i],v[i]这个物品肯定·放不进去;肯定取上一个.
  p[j]=MAX(p[j-volume[i]]+value[i],p[j]);

中国剩余定理模板

ACM板子_第1张图片

/*中国剩余定理模板*/
ll ai[maxn],bi[maxn];
//扩展欧几里得:一定存在整数 x, y 满足等式 a * x + b * y = gcd(a,b)
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0){ x=1, y=0; return a;}
    ll gcd=exgcd(b,a%b,x,y);
    ll tp=x;
    x=y, y=tp-a/b*y;
    return gcd;
}
ll mult(ll a,ll b,ll mod){
    long long res=0;
    while(b>0){
        if(b&1) res=(res+a)%mod;
        a=(a+a)%mod;
        b>>=1;
    }
    return res;
}
ll excrt(){
    ll x,y;
    ll ans=bi[1],M=ai[1];
    for(int i=2;i<=n;++i){
        ll a=M,b=ai[i],c=(bi[i]-ans%ai[i]+ai[i])%ai[i];
        ll gcd=exgcd(a,b,x,y);
        x=mult(x,c/gcd,b/gcd);
        ans+=x*M;
        M*=b/gcd;
        ans=(ans%M+M)%M;
    }
    return (ans%M+M)%M;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;++i)
        cin>>ai[i]>>bi[i];
    cout<

Lucas定理。

ACM板子_第2张图片

ACM板子_第3张图片

扩展Lucas定理

ACM板子_第4张图片

typedef long long ll;
//快速幂
ll quick_mod(ll a,ll b,ll m){
    ll ans=1ll;
    while(b){
        if(b&1) ans=ans*a%m;
        b>>=1;
        a=a*a%m;
    }
    return ans;
}
//扩展欧几里得求逆元
ll exgcd(ll a,ll b,ll &x,ll &y){
    if(a%b==0){
        x=0ll;y=1ll;
        return b;
    }
    ll v,tx,ty;
    v=exgcd(b,a%b,tx,ty);
    x=ty;
    y=tx-a/b*ty;
    return v;
}

//
ll inv(ll a,ll p){
    if(!a) return 0ll;
    ll x,y;
    exgcd(a,p,x,y);
    x=(x%p+p)%p;
    return x;
}

ll Mul(ll n,ll pi,ll pk){
    if(!n) return 1ll;
    ll ans=1ll;
    for(ll i=2;i<=pk;i++)
        if(i%pi) ans=ans*i%pk;
    ans=quick_mod(ans,n/pk,pk);
    for(ll i=2;i<=n%pk;i++){
        if(i%pi) ans=ans*i%pk;
    }
    return ans*Mul(n/pi,pi,pk)%pk;
}

ll exlucas(ll n,ll m,ll p,ll pi,ll pk){
    if(m>n) return 0ll;
    ll a=Mul(n,pi,pk),b=Mul(m,pi,pk),c=Mul(n-m,pi,pk);
    ll k=0ll,ans=0ll;
    for(ll i=n;i;i/=pi) k+=i/pi;
    for(ll i=m;i;i/=pi) k-=i/pi;
    for(ll i=n-m;i;i/=pi) k-=i/pi;
    ans=a*inv(b,pk)%pk*inv(c,pk)%pk*quick_mod(pi,k,pk)%pk;
    return ans*(p/pk)%p*inv(p/pk,pk)%p;     //中国剩余定理  a[i]*M*x  余数*其他个个素数的乘积*x
}

int main()
{
    ll n,m,p,ans=0;
    while(cin>>n>>m>>p){
        for(ll x=p,i=2;i<=p;i++){
            if(x%i==0){
                ll pk=1ll;
                while(x%i==0) pk*=i,x/=i;
                ans=(ans+exlucas(n,m,p,i,pk))%p;
            }
        }
        cout<

hash+二分 求最长回文串

#pragma GCC optimize(2)
using namespace std;
 
typedef long long ll;
const int MAXLEN = 1000005;
const int MAXNUM = 35;
int strnum=0;		//字符串的数量
//正序和逆序的字符串
string s1, s2;
//结果
int result[MAXNUM] = { 0 };
//进制数
ll p = 10000019;
//mod
ll mod = 1000000007;
//H数组,H[i]表示字符串前i个字符的子串的hash值
ll H1[MAXLEN] = { 0 }, H2[MAXLEN] = { 0 };
//powp[i]表示p的i次方
ll powp[MAXLEN] = { 0 };
 
//计算字符串的H数组
void calH(ll* H,string& str) {
	H[0] = str[0];
	for (int i = 1; i < str.size(); i++) {
		H[i] = (H[i - 1] * p + str[i]) % mod;
	}
}
 
//打表,预先求出p的幂次
void getpowp() {
	powp[0] = 1;
	for (int i = 1; i < MAXLEN; i++) {
		powp[i] = (powp[i - 1] * p)%mod;
	}
}
 
//计算子串的哈希值
ll calsubH(ll* H,int i,int j){
	//对i为0的情况特殊处理
	if (i == 0)
		return H[j];
	else
		return ((H[j] - H[i - 1] * powp[j - i + 1]) % mod + mod) % mod;
}
 
//比较两个子串是否相等
bool cmp(int i1,int j1,int i2,int j2) {
	return calsubH(H1, i1, j1) == calsubH(H2, i2, j2);	
}
 
//对字符串进行预处理,每两个字符之间插入一个'#'
void initstring(string& tmp,int& len) {
	len = 2 * len - 1;
	char* ch = new char[len + 1];
	for (int i = 0; i < len; i++) {
		if (i % 2 == 0)	ch[i] = tmp[i / 2];
		else    ch[i] = '#';
	}
	ch[len] = '\0';
	tmp = string(ch);
}
 
//计算以第i个字符为中心时,回文子串的最大长度
//字符串s1,字符串s2为其反序
int calbyi(int i,int len) {
	//搜索区间的最小值、最大值
	int lb = 0, rb = min(i, len - i-1);	
	//字串的半径(不包括中心点),注意必须初始化应对i=0的情况
	int k=0;
	
	//使用二分法求解
	while (rb > lb) {
		//注意半径要大于等于1,否则会在第一个分支中死循环
		k = (lb + rb) / 2+1;
		//正、反序字串中要比较的子串的开始位置
		int b1 = i - k, b2 = len - k - (i + 1);
		//判断子串哈希值是否相等
		if (cmp(b1,b1+k-1,b2,b2+k-1)) {
			//以i为中心,k为半径的子串是回文子串,可以向两边拓展(用二分的方法)
			//所以修改区间的最小值,表示半径最少有k个字符
			lb = k;
		}
		else {
			//以i为中心,k为半径的子串不是回文子串,所以向中心收缩半径
			//修改区间的最大值,表示半径要小于k个字符
			rb = k - 1;
		}		
	}
 
	//求除掉'#'后的回文子串的长度,注意这里半径不能用k,因为上面退出循环时可能k的值大于正确半径
	//要根据中心字符的类型来判断
	//中心为'#',回文子串为偶数长度
	if (s1[i] == '#') {
		return ((lb + 1) / 2) * 2;
	}
	//中心为英文字符,回文串为奇数长度
	else{
		return 2*(lb/2) + 1;
	}
}
 
//计算字符串s1的最长回文字串长度
int cal(int len) {
	int maxlen=1;	//回文串的最大长度
	//遍历所有点,将其作为中心点求解
	for (int i = 0; i < len; i++) {
		maxlen = max(calbyi(i,len), maxlen);
	}
	return maxlen;
}
 
 
int main(void) {
	ios::sync_with_stdio(false);
	getpowp();		//打表
 
	//给s1,s2预留空间来优化
	s1.reserve(MAXLEN);
	s2.reserve(MAXLEN);
 
	//对所有字符串求解其最长回文子串长度
	string inputstr;
	int strnum = 0;
	int len = 0;
	while(true) {
		getline(cin, s1);
		if (s1 == "END")		break;
		//原始字符串和反序字符串
		len = s1.size();
		initstring(s1,len);		//给字符之间插入'#'
		s2 = s1;
		reverse(s2.begin(), s2.end());
		//计算两个字符串的H数组
		calH(H1, s1);
		calH(H2, s2);
		//求解
		result[++strnum]=cal(len);
	}
	
	//输出结果
	for (int i = 1; i <= strnum; i++) {
		cout << "Case " << i << ": " << result[i] << endl;
	}
}

尼姆博弈模型

尼姆博弈模型,大致上是这样的:
有3堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取1个,多者不限(但不能超过一堆),最后取光者得胜。

所以取到(0,0,0,0)就获胜了 ---- 异或和不等零 先手必胜

莫队算法

/*一般来说,如果可以在 O(1) 内从 [l,r] 的答案转移到 [l-1,r] 、 [l+1,r] 、 [l,r-1] 、 [l,r+1] 这四个与之紧邻的区间的答案,则可以考虑使用莫队
转移分为两种情况,往区间里添数,或者往区间里删数,所以可以写成两个函数:del() , add()
*/
int ll,rr,bs;
lll res=0;

struct node{
    int l,r;
    int id;
}p[N];

bool cmp(node a,node b){
    if(a.l/bs == b.l/bs) return a.r < b.r;
    return a.l < b.l;
}

lll cal(lll x){
    return 1ll*x*(x-1)/2;
}

void del(int x){
    cnt[x&1][a[x]]--;
    res -= (cnt[x&1][a[x]]);
}

void add(int x){
    res += (cnt[x&1][a[x]]);
    cnt[x&1][a[x]]++;
}

void slove(int i){
    while(ll < p[i].l) del(ll++);
    while(ll > p[i].l) add(--ll);
    while(rr < p[i].r) add(++rr);
    while(rr > p[i].r) del(rr--);
    ans[p[i].id] = cal(p[i].r - p[i].l + 1) - res;
}

signed main(){
   // ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    scanf("%lld%lld", &n, &q);
    bs = sqrt(n);
    for(int i = 1; i <= n; i++) {
       scanf("%lld", &a[i]);
        a[i]--;
        a[i] = a[i] ^ a[i-1];
    }
    for(int i = 1; i <= q; i++){
         scanf("%lld%lld", &p[i].l, &p[i].r);
        p[i].l--; //本题用的是异或和的前缀和 所以求[l,r]区间时 是用到 [l-1,r]
        p[i].id = i;
    }
    sort(p + 1, p + 1 + q, cmp);
    ll = 1, rr = 0;
    for(int i = 1; i <= q; i++){
        slove(i);
    }
    for(int i = 1; i <= q; i++) 
        printf("%lld\n",ans[i]);
    return 0; 
}

权值线段树

/*
权值线段树一般用于维护一段区间的数出现的次数,从它的定义来看,它可以快速计算出一段区间的数的出现次数。

在实际应用中,我们使用权值线段树查询区间第K大的值。

以数的大小为区间 数x要在[1,n]内

tree[rt] 插入数x 找到x在树中位置rt  次数数组tree[rt]++
*/

//单点更新
//类似基础线段树,递归到叶子节点+1然后回溯
void add(int l, int r, int rt, int x){
    if (l == r) tree[rt]++;
    else{
        int mid = (l + r) >> 1;
        if (x <= mid) add(l, mid, rt << 1, x);
        else add(mid + 1, r, rt << 1 | 1, x);
        tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
    }
}


//查询第K大
int query_kth(int pos, int l, int r, int k){ //如果K比右儿子大,就说明第K大的数在左子树中。然后,K要减去右子节点的值。
    if(l == r) return l;
    int mid = (l + r) >> 1, right = tree[pos << 1 | 1];
    if(k <= right) return query(pos << 1 | 1, mid + 1, r, k);
    else return query(pos << 1, l, mid, k - right);
}
//查询第K小
int query_kth(int pos, int l, int r, int k){
    if(l == r) return l;
    int mid = (l + r) >> 1, left = tree[pos << 1];
    if(k <= left) return query(pos << 1, l, mid, k - left);
    else return query(pos << 1 | 1, mid + 1, r, k);
}

//查询某个数出现次数
int find(int l, int r, int v, int x){
    if (l == r) return tree[v];
    else{
        int mid = (l + r) >> 1;
        if (x <= mid) return find(l, mid, v << 1, x);
        else return find(mid + 1, r, v << 1 | 1, x);
    }
}

//查询一段区间数出现的次数
int find(int l, int r, int v, int x, int y){
    if (l == x && r == y) return tree[v];
    else{
        int mid = (l + r) >> 1;
        if (y <= mid) return find(l, mid, v << 1, x, y);
        else if (x > mid) return find(mid + 1, r, v << 1 | 1, x, y);
        else return find(l, mid, v << 1, x, mid) + find(mid + 1, r, v << 1 | 1, mid + 1, y);
    }
}

//区间离散化
int get_discretization(){
    for (int i = 1; i <= n; ++i) a[i] = read(), b[i] = a[i]; //读入数据
    sort(b + 1, b + n + 1);
    int len = unique(b + 1, b + n + 1) - b - 1;
    for (int i = 1; i <= n; ++i){
        int pos = lower_bound(b + 1, b + len + 1, a[i]) - b;
        a[i] = pos;
    } //离散化
}

回文树

1.len[i]表示编号为i的节点表示的回文串的长度(一个节点表示一个回文串)

2.next[i][c]表示编号为i的节点表示的回文串在两边添加字符c以后变成的回文串的编号(和字典树类似)。

3.fail[i]表示节点i失配以后跳转不等于自身的节点i表示的回文串的最长后缀回文串(和AC自动机类似)。

4.cnt[i]表示节点i表示的本质不同的串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的)

5.num[i]表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数。

6.last指向新添加一个字母后所形成的最长回文串表示的节点。

7.S[i]表示第i次添加的字符(一开始设S[0] = -1(可以是任意一个在串S中不会出现的字符))。

8.p表示添加的节点个数。

9.n表示添加的字符个数。


map< pair ,int> mp,ct;
int has1[M],has2[M];
int bas1[M],bas2[M];
int p1,p2;
const int mod1=3145739;//如有误差手动修改
const int mod2=6291469;

void init_base(){
	p1=2333;//如有误差手动修改
	p2=17;
	bas1[0]=bas2[0]=1;
	// MXN=3e5+7
	for(int i=1;i= 0 ; -- i ) cnt[fail[i]] += cnt[i] ;
		//父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
	}
} ;

你可能感兴趣的:(cocoa,macos,objective-c,c++)