ACM现场赛打印模板

写一个最大公约数gcd和最小公倍数lcm和扩展欧几里得exgcd的比较简单的模板

int gcd(int a,int b){
	return b?gcd(b,a%b):a;
}
 
 
int lcm(int a,int b){
	return a/gcd(a,b)*b;
}
 
int exgcd(int a,int b,int &x,int &y){//ax+by = gcd(a,b);(1)
    if(b == 0){
        x = 1;y = 0;
        return a;
    }
    int c = exgcd(b,a%b,y,x);//转化为b*x2+(a%b)*y2 = gcd(b,a%b) = gcd(a,b);(2)
    //(2)式与(1)相等,则x = y2,y = x2-[a/b]*y2;迭代回去
    y = y-a/b*x;
    return c;

O(n),线性筛,大致思路就是我们在考虑删除两个不同的数字的时候,实际上可以同过计数来实现,而不是真正的删除。 在遍历数组的时候保存两个值:一个是数组中的一个数字,一个是次数。当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加1。如果下一个数字和我们之前保存的数字不同,则次数减1。如果次数为零,我们需要保存下一个数字,并把次数设为1。由于我们要找的数字出现的次数比其他所有数字出现的次数之和还要多,那么要找的数字肯定是最后一次把次数设为1时对应的数字。


ll findmostappear(ll *a,ll len)
{
    ll candidate=0,count=0;
    for(ll i=0;i<len;i++)
    {
        if(count==0)
        {
            candidate=a[i];
            count=1;
        }
        else
        {
            if(candidate==a[i])
                count++;
            else
                count--;
        }
    }
    return candidate;
}

贴一个最长上升子序列的模板,这大概是poj2533的题解:


#include <iostream>
#include <cstdio>
#include <algorithm>
 
using namespace std;
int a[1005],dp[1005],n;
 
 
int main()
{
    scanf ("%d", &n);
    if (n == 0){
        printf ("1\n");
        return 0;
    }
    for (int i = 1; i <= n; i++)
         scanf ("%d",&a[i]);
    int i, j;
    for (i = 1; i <= n; i++) {
        dp[i] = 1;
        for (j = 1; j < i; j++) {
             if(a[i] > a[j])
                dp[i] = max(dp[i],dp[j] + 1);
        }
    }
    int ans = 0;
    for (i = 1; i <= n; i++)
        if (dp[i] > ans) ans = dp[i];
    printf ("%d\n",ans);
    return 0;
}

//O(nlogn)解法,贪心+二分
	考虑一个简单的贪心,如果我们要使上升子序列尽可能的长,则我们需要让序列上升得尽可能慢,因此我们希望每次在上升子序列最后加上的那个数尽可能的小。
基于上面的贪心思路,我们维护一个数组 d[i] ,表示长度为 i 的最长上升子序列的末尾元素的最小值,用 len 记录目前最长上升子序列的长度,起始时 len 为 1,d[1]=nums[0]。
同时我们可以注意到 d[i] 是关于 i 单调递增的。因为如果 d[j]≥d[i] 且 j<i,我们考虑从长度为 i 的最长上升子序列的末尾删除 i−j 个元素,那么这个序列长度变为 j ,且第 j 个元素 x(末尾元素)必然小于 d[i],也就小于 d[j]。那么我们就找到了一个长度为 j 的最长上升子序列,并且末尾元素比 d[j] 小,从而产生了矛盾。因此数组 d 的单调性得证。
我们依次遍历数组 nums 中的每个元素,并更新数组 d 和 len 的值。如果 nums[i]>d[len] 则更新 len=len+1,否则在 d[1…len]中找满足 d[i−1]<nums[j]<d[i] 的下标 i,并更新 d[i]=nums[j]。
根据 d 数组的单调性,我们可以使用二分查找寻找下标 i,优化时间复杂度。
最后整个算法流程为:
设当前已求出的最长上升子序列的长度为 len(初始时为 1),从前往后遍历数组 nums,在遍历到 nums[i] 时:
如果 nums[i]>d[len] ,则直接加入到 d 数组末尾,并更新 len=len+1;
否则,在 d 数组中二分查找,找到第一个比 nums[i] 小的数 d[k] ,并更新 d[k+1]=nums[i]class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int len = 1, n = (int)nums.size();
        if (n == 0) {
            return 0;
        }
        vector<int> d(n + 1, 0);
        d[len] = nums[0];
        for (int i = 1; i < n; ++i) {
            if (nums[i] > d[len]) {
                d[++len] = nums[i];
            } else {
                int l = 1, r = len, pos = 0; // 如果找不到说明所有的数都比 nums[i] 大,此时要更新 d[1],所以这里将 pos 设为 0
                while (l <= r) {
                    int mid = (l + r) >> 1;
                    if (d[mid] < nums[i]) {
                        pos = mid;
                        l = mid + 1;
                    } else {
                        r = mid - 1;
                    }
                }
                d[pos + 1] = nums[i];
            }
        }
        return len;
    }
};

线段树,这份模板大概可以做到单点更新值和区间求和


const int maxn = 50005;
 
struct node{
    int l,r,sum;
}tree[maxn*9];
 
int a[maxn];
 
void build(int l,int r,int index){
    tree[index].l = l;
    tree[index].r = r;
    if(l == r){
        tree[index].sum = a[l];
        return;
    }
    int middle = (l+r)/2;
    build(l,middle,2*index);
    build(middle+1,r,2*index+1);
    tree[index].sum = tree[2*index].sum + tree[2*index+1].sum;
}
 
void update(int number,int index,int ans){
    tree[index].sum += ans;
    if(tree[index].l == tree[index].r&&tree[index].l == number)
        return ;
    int middle = (tree[index].l+tree[index].r)/2;
    if(middle >= number) update(number,2*index,ans);
    else update(number,2*index+1,ans);
}
 
int getsum(int l,int r,int index){
    if(tree[index].l == l&&tree[index].r == r)
        return tree[index].sum;
    int middle = (tree[index].l+tree[index].r)/2;
    if(middle < l) return getsum(l,r,2*index+1);
    else if(middle >= r) return getsum(l,r,2*index);
    else return getsum(l,middle,2*index)+getsum(middle+1,r,2*index+1);
}

贴一个康拓展开和逆康拓展开的模板吧,康拓展开就是给你一个字符串让你算按照字典序排这个字符串应该出现在什么地方,逆康拓就是给你位置,让你算这个字符串是什么:

康拓展开:

ll cantor(char str[]){
    int len = strlen(str);
    ll ans = 0;
    for(int i = 0;i < len;i++){
        int tmp = 0;
        for(int j = i+1;j < len;j++)
            if(str[j] < str[i]) tmp++;
        ans += tmp*f[len-i-1];
    }
    return ans;
}

逆康拓展开(这里需要注意的模板里面写的13是这个字符串的长度,然后模板中的代码是小写的字母排的字典序):

void reverse_cantor(ll n,ll k){
    ll i,j,t,vis[13] = {0};
    char s[13];
    k--;
    for(i = 0;i < n;i++){
        t = k/f[n-i-1];
        for(j = 0;j < n;j++){
            if(!vis[j]){
                if(t == 0) break;
                t--;
            }
        }
        s[i] = j+'a';
        vis[j] = 1;
        k %= f[n-i-1];
        cout<<s[i];
    }
    cout<<endl;
}

floyd算法与dijkstra算法的区别就是在floyd算法中可以有限负权边


while(cin>>n>>m){
        for(int i = 0;i < n;i++)
            for(int j  = 0;j < n;j++){
                if(i == j) dp[i][j] = 0;
                else dp[i][j] = 1e9;
            }
        while(m--){
            cin>>x>>y>>z;
            dp[x][y] = min(dp[x][y],z);
            dp[y][x] = min(dp[y][x],z);
        }
        cin>>s>>t;
        for(int k = 0;k < n;k++)
            for(int i = 0;i < n;i++)
                for(int j = 0;j < n;j++)
                    dp[i][j] = min(dp[i][j],dp[i][k]+dp[k][j]);
        if(dp[s][t] == 1e9) cout<<"-1"<<endl;
        else cout<<dp[s][t]<<endl;
    }

SPFA

#include <bits/stdc++.h>
 
using namespace std;
const int maxn = 205;
 
int n,m;
int d[maxn],inq[maxn];
vector<pair<int ,int > >E[maxn];
 
void init(){
    for(int i = 0;i < maxn;i++) E[i].clear();
    for(int i = 0;i < maxn;i++) inq[i] = 0;
    for(int i = 0;i < maxn;i++) d[i] = 1e9;
}
 
int main(){
    while(cin>>n>>m){
        init();
        for(int i = 0;i < m;i++){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            E[x].push_back(make_pair(y,z));
            E[y].push_back(make_pair(x,z));
        }
        int s,t;
        scanf("%d%d",&s,&t);
        queue<int>Q;
        Q.push(s),d[s] = 0,inq[s] = 1;
        while(!Q.empty()){
            int now = Q.front();
            Q.pop();inq[now] = 0;
            for(int i = 0;i < E[now].size();i++){
                int v = E[now][i].first;
                if(d[v] > d[now]+E[now][i].second){
                    d[v] = d[now]+E[now][i].second;
                    if(inq[v] == 1) continue;
                    inq[v] = 1;
                    Q.push(v);
                }
            }
        }
        if(d[t] == 1e9) cout<<"-1"<<endl;
        else cout<<d[t]<<endl;
    }
    return 0;
}

卡特兰数:
1 通项公式:h(n)=C(n,2n)/(n+1)=(2n)!/((n!)*(n+1)!)

2递推公式:h(n)=((4*n-2)/(n+1))*h(n-1); h(n)=h(0)*h(n-1)+h(1)*h(n-2)+…+h(n-1)*h(0).

3前几项为:h(0)=1,h(1)=1,h(2)=2,h(3)=5,h(4)=14,h(5)=42,……

4应用场景:

a.括号化问题。
  矩阵链乘: P=a1×a2×a3×……×an,依据乘法结合律,不改变其顺序,只用括号表示成对的乘积,试问有几种括号化的方案?(h(n)种)
b.出栈次序问题。
  一个栈(无穷大)的进栈序列为1,2,3,…n,有多少个不同的出栈序列?
  类似:
  (1)有2n个人排成一行进入剧场。入场费5元。其中只有n个人有一张5元钞票,另外n人只有10元钞票,剧院无其它钞票,问有多少中方法使得只要有10元的人买票,售票处就有5元的钞票找零?(将持5元者到达视作将5元入栈,持10元者到达视作使栈中某5元出栈)
  (2)在圆上选择2n个点,将这些点成对连接起来,使得所得到的n条线段不相交的方法数。

c.将多边行划分为三角形问题。
  (1)将一个凸多边形区域分成三角形区域的方法数?
  (2)类似:一位大城市的律师在她住所以北n个街区和以东n个街区处工作。每天她走2n个街区去上班。如果她从不穿越(但可以碰到)从家到办公室的对角线,那   么有多少条可能的道路?
  (3)类似:在圆上选择2n个点,将这些点成对连接起来使得所得到的n条线段不相交的方法数?
d.给顶节点组成二叉树的问题。
  给定N个节点,能构成多少种形状不同的二叉树?
  (一定是二叉树!先去一个点作为顶点,然后左边依次可以取0至N-1个相对应的,右边是N-1到0个,两两配对相乘,就是h(0)*h(n-1) + h(2)*h(n-2) + + h(n-1)h(0)=h(n))(能构成h(N)个)。

这是一个卡特兰数的模板,而且可以去求大数卡特兰:

#include<stdio.h>
//h( n ) = ( ( 4*n-2 )/( n+1 )*h( n-1 ) );
//*******************************

//打表卡特兰数

//第 n个 卡特兰数存在a[n]中,a[n][0]表示长度;

//注意数是倒着存的,个位是 a[n][1] 输出时注意倒过来。

//*********************************
int a[105][100];
void ktl()
{
    int i,j,yu,len;
    a[2][0]=1;
    a[2][1]=2;
    a[1][0]=1;
    a[1][1]=1;
    len=1;
    for(i=3;i<101;i++)
    {
        yu=0;
        for(j=1;j<=len;j++)
        {
            int t=(a[i-1][j])*(4*i-2)+yu;
            yu=t/10;
            a[i][j]=t%10;
        }   
        while(yu)
        {
            a[i][++len]=yu%10;
            yu/=10;
        }
        for(j=len;j>=1;j--)
        {
            int t=a[i][j]+yu*10;
            a[i][j]=t/(i+1);
            yu = t%(i+1);
        }       
        while(!a[i][len])
        {
            len--;
        }   
        a[i][0]=len;
    }   
}   
int main()
{
    ktl();
    int n;
    while(scanf("%d",&n)!=EOF)
    {
    for(int i=a[n][0];i>0;i--)
        {
            printf("%d",a[n][i]);
        }   
        puts("");
    }   
    return 0;
}  

贴一个N皇后问题的板子,ans保存的是第i个方案(按字典序排)的第j列的位置(一个数字,代表在第几列),也就是路径,ind代表的是方案数,初始化它为1,所以结果需要减1,row,dia1,dia2代表的某一行,某一列,某一斜线有没有被访问过,所以需要全部初始化为true,dfs是从1开始的

int n,ind,ans[1500][100];
bool row[1200],dia1[2500],dia2[2500];
 
void dfs(int q){
    if(q > n){
        ind++;
        for(int i = 1;i <= n;i++)
            ans[ind][i] = ans[ind-1][i];
        return ;
    }
    for(int r = 1;r <= n;r++){
        if(row[r]&&dia1[r+q]&&dia2[n-q+r]){
            row[r] = dia1[r+q] = dia2[n-q+r]=false;
            ans[ind][q] = r;
            dfs(q+1);
            row[r] = dia1[r+q] = dia2[n-q+r]=true;
        }
    }
}

举个例子,给你一个含有n个数的集合,找出质因数全部属于这个集合的第k个数,然后下面就是模板

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int a[100100],b[110],c[110];
    int k,n,i,j,mi;
    scanf("%d%d",&k,&n);
    for(i=0;i<k;i++){
        scanf("%d",&b[i]);
        c[i]=0;
    }
    a[0]=1;
    for(i=1;i<=n;i++){
        mi=2e9;
        for(j=0;j<k;j++){
            while(a[c[j]]*b[j]<=a[i-1])
                c[j]++;
            mi=min(mi,a[c[j]]*b[j]);
        }
        a[i]=mi;
    }
    printf("%d\n",a[n]);
    return 0;
}

线段树大模板

#include <bits/stdc++.h>
#define MAXN 100010
#define inf 0x3f3f3f3f
typedef long long ll;

 struct node{
     ll l,r;//区间[l,r]
     ll add;//区间的延时标记
     ll sum;//区间和
     ll mx; //区间最大值
     ll mn; //区间最小值
 }tree[MAXN<<2];//一定要开到4倍多的空间

 void pushup(ll index){
     tree[index].sum = tree[index<<1].sum+tree[index<<1|1].sum;
     tree[index].mx = max(tree[index<<1].mx,tree[index<<1|1].mx);
     tree[index].mn = min(tree[index<<1].mn,tree[index<<1|1].mn);
 }
 void pushdown(ll index){
     //说明该区间之前更新过
     //要想更新该区间下面的子区间,就要把上次更新该区间的值向下更新
     if(tree[index].add > 0){
         //替换原来的值
         /*
         tree[index<<1].sum = (tree[index<<1].r-tree[index<<1].l+1)*tree[index].add;
         tree[index<<1|1].sum = (tree[index<<1|1].r-tree[index<<1|1].l+1)*tree[index].add;
         tree[index<<1].mx = tree[index].add;
         tree[index<<1|1].mx = tree[index].add;
         tree[index<<1].mn = tree[index].add;
         tree[index<<1|1].mn = tree[index].add;
         tree[index<<1].add = tree[index].add;
         tree[index<<1|1].add = tree[index].add;
         tree[index].add = 0;*/
         //在原来的值的基础上加上val

         tree[index<<1].sum += (tree[index<<1].r-tree[index<<1].l+1)*tree[index].add;
         tree[index<<1|1].sum +=(tree[index<<1|1].r-tree[index<<1|1].l+1)*tree[index].add;
         tree[index<<1].mx += tree[index].add;
         tree[index<<1|1].mx += tree[index].add;
         tree[index<<1].mn += tree[index].add;
         tree[index<<1|1].mn += tree[index].add;
         tree[index<<1].add += tree[index].add;
         tree[index<<1|1].add += tree[index].add;
         tree[index].add = 0;

     }
 }
 void build(ll l,ll r,ll index){
     tree[index].l = l;
     tree[index].r = r;
     tree[index].add = 0;//刚开始一定要清0
     if(l == r){
         tree[index].sum = 0;
         tree[index].mn = tree[index].mx = tree[index].sum;
         return ;
     }
     ll mid = (l+r)>>1;
     build(l,mid,index<<1);
     build(mid+1,r,index<<1|1);
     pushup(index);
 }
 void updata(ll l,ll r,ll index,ll val){
     if(l <= tree[index].l && r >= tree[index].r){
         /*
         把原来的值替换成val,因为该区间有tree[index].r-tree[index].l+1
         个数,所以区间和 以及 最值为:
         */
         /*tree[index].sum = (tree[index].r-tree[index].l+1)*val;
         tree[index].mn = val;
         tree[index].mx = val;
         tree[index].add = val;//延时标记*/
         /*
         在原来的值的基础上加上val,因为该区间有tree[index].r-tree[index].l+1
         个数,所以区间和 以及 最值为:
         */
         tree[index].sum += (tree[index].r-tree[index].l+1)*val;
         tree[index].mn += val;
         tree[index].mx += val;
         tree[index].add += val;//延时标记

         return ;
     }
     pushdown(index);
     ll mid = (tree[index].l+tree[index].r)>>1;
     if(l <= mid){
         updata(l,r,index<<1,val);
     }
     if(r > mid){
         updata(l,r,index<<1|1,val);
     }
     pushup(index);
 }
 ll query(ll l,ll r,ll index){
     if(l <= tree[index].l && r >= tree[index].r){
         //return tree[index].sum;
         return tree[index].mx;
         //return tree[index].mn;
     }
     pushdown(index);
     ll mid = (tree[index].l+tree[index].r)>>1;
     ll ans = 0;
     ll Max = 0;
     ll Min = inf;
     if(l <= mid){
         //ans += query(l,r,index<<1);
         Max = max(query(l,r,index<<1),Max);
         //Min = min(query(l,r,index<<1),Min);
     }
     if(r > mid){
         //ans += query(l,r,index<<1|1);
         Max = max(query(l,r,index<<1|1),Max);
         //Min = min(query(l,r,index<<1|1),Min);
     }
     //return ans;
     return Max;
     //return Min;
 }

int main()
{
    int n,m,q,x,y,z;
    while(~scanf("%d%d",&n,&m)){
        build(1,n,1);
        while(m--){
            scanf("%d",&q);
            if(q == 1){
                cout<<"查询:(x,y)"<<endl;
                scanf("%d %d",&x,&y);
                cout<<query(x,y,1)<<endl;
            }
            else{
                cout<<"更新(x,y)为z:"<<endl;
                scanf("%d %d %d",&x,&y,&z);
                updata(x,y,1,z);
                for(int i = 1; i <= n; ++i){
                    printf("a[%d] = %d\n",i,query(i,i,1));
                }
            }
        }
    }
    return 0;
}


输入输出挂:

template <class T>
inline bool scan_d(T &ret)
{
    char c;
    int sgn;
    if (c = getchar(), c == EOF)
    {
        return 0; //EOF
    }
    while (c != '-' && (c < '0' || c > '9'))
    {
        c = getchar();
    }
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while (c = getchar(), c >= '0' && c <= '9')
    {
        ret = ret * 10 + (c - '0');
    }
    ret *= sgn;
    return 1;
}

void Out(ll a)
{    //  输出外挂
    if (a < 0)
    {
        putchar('-');
        a = -a;
    }
    if (a >= 10)
    {
       Out(a / 10);
    }
    putchar(a % 10 + '0');
}

贴一个任意进制(2-36)到10进制的转换

#include <iostream>
#include <string.h>
#include <stdio.h>
 
using namespace std;
 
long  toTen(char a[], int bit)
{
    int length = strlen(a);
    int i, b=1,sum=0; //i要做数组a的下标,注意其起止范围
    for(i=length-1; i>=0; i-- )
    {
        if(a[i]>='A')
        {
            sum += (a[i]-'A'+10) *b;
            b *= bit;
        }
        else
        {
            sum += (a[i]-'0') *b;
            b *= bit;
        }
    }
    return sum;
}
int main()
{
    int aNum,a;
    char cNum[20];
    cin>>aNum;
    sprintf(cNum,"%d",aNum);
    a = toTen(cNum,5)//这里是5进制转10进制的模板
    return 0;
}

最长公共子序列

#include<bits/stdc++.h>
 
using namespace std;
 
const int maxn = 1111;
 
int dp[maxn][maxn]={0};
string a,b;
 
void LCS(int n,int m)
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        if(a[i-1]==b[j-1]) dp[i][j]=dp[i-1][j-1]+1;
        else dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
}
 
int main()
{
    cin>>a>>b;
    int l1=a.size(),l2=b.size();
    LCS(l1,l2);
    int len=dp[l1][l2];
    string ans;
    int i=l1,j=l2;
    while(dp[i][j])
    {
        if(dp[i][j]==dp[i-1][j]) i--;
        else if(dp[i][j]==dp[i][j-1]) j--;
        else ans.push_back(a[i-1]),i--,j--;
    }
    for(int i=len-1;i>=0;i--)
        printf("%c",ans[i]);
    return 0;
}

最长上升(不下降)子序列和最长下降(不上升)子序列

#include <bits/stdc++.h>
 
using namespace std;
 
int main()
{
    int T,n,i,j,k,a[100005],k1,k2,ans[100005],len,b[100005],flag;
    scanf("%d",&T);
    while(T--)
    {
        flag=0;
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        memset(ans,0,sizeof(ans));//ans数组存的是最大子序列终点的数字
        k1=k2=0;
        scanf("%d",&n);
        for(i=1; i<=n; i++)
            scanf("%d",&a[i]);
        //判断是否是上升子序列
        ans[1]=a[1];
        len=1;
        for(i=2; i<=n; i++)
        {
            if(a[i]>=ans[len])
                ans[++len]=a[i];
 
            else
            {
                int pos=upper_bound(ans+1,ans+len+1,a[i])-ans;
                ans[pos]=a[i];
            }
        }
        if(len==n-1||len==n)
        {
            printf("YES\n");
            continue;
        }
        //判断是否是下降子序列
        for(i=1; i<=n; i++)
            b[i]=a[n-i+1];
        ans[1]=b[1];
        len=1;
        for(i=2; i<=n; i++)
        {
            if(b[i]>=ans[len])
                ans[++len]=b[i];
 
            else
            {
                int pos=upper_bound(ans+1,ans+len+1,b[i])-ans;
                ans[pos]=b[i];
            }
        }
        if(len==n-1||len==n)
            printf("YES\n");
        else printf("NO\n");
 
    }
    return 0;
}

归并排序

#define max 1000001
 
long long a[max],b[max];
long long count;
void Merge(long long a[], int start, int mid, int end)   //归并排序的合并部分
{
    int i = start,j = mid + 1,k = start;
    while(i <= mid&&j <= end)
    {
        if(a[i] <= a[j])
        {
            b[k++] = a[i++];
        }
        else
        {
            count += j - k;
            b[k++] = a[j++];
        }
    }
    while(i <= mid)
    {
        b[k++] = a[i++];
    }
    while(j <= end)
    {
        b[k++] = a[j++];
    }
    for(int i = start; i <= end; i++)
    {
        a[i] = b[i];
    }
}
 
void MergeSort(long long a[], int start, int end)  //归并排序
{
    if(start < end)
    {
        int mid = (start + end)/2;
        MergeSort(a,start,mid);     // 将前半部分排序
        MergeSort(a,mid+1,end);     // 将后半部分排序
        Merge(a,start,mid,end);     // 合并前后两个部分
    }
}


写一些博弈模板,借用51Nod上的题目:

Bash游戏:

有一堆石子共有N个。A B两个人轮流拿,A先拿。每次最少拿1颗,最多拿K颗,拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出N和K,问最后谁能赢得比赛。


#include <bits/stdc++.h>

using namespace std;

int main(){
    int t,n,k;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&k);
        if(n <= k) printf("A\n");
        else{
            if(n%(k+1) == 0) printf("B\n");
            else printf("A\n");
        }
    }
    return 0;
}

Bash游戏V2:

有一堆石子共有N个。A B两个人轮流拿,A先拿。每次只能拿1,3,4颗,拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出N,问最后谁能赢得比赛。


#include <bits/stdc++.h>


using namespace std;

int main(){
    int t,n;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        if(n%7 == 0||n%7 == 2) printf("B\n");
        else printf("A\n");
    }
    return 0;
}

BashV3:有一堆石子共有N个。A B两个人轮流拿,A先拿。每次拿的数量只能是2的正整数次幂,比如(1,2,4,8,16…),拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出N,问最后谁能赢得比赛


#include <bits/stdc++.h>

using namespace std;

char s[1005];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s",s);
        int n=strlen(s);
        int ans=0;
        for(int i=0;i<n;i++)
        {
            ans=ans+(s[i]-'0');
        }
        if(ans%3)
        printf("A\n");
        else
        printf("B\n");
    }
    return 0;
}

Bash游戏V4:有一堆石子共有N个。A B两个人轮流拿,A先拿。每次拿的数量最少1个,最多不超过对手上一次拿的数量的2倍(A第1次拿时要求不能全拿走)。拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出N,问最后谁能赢得比赛。


#include <bits/stdc++.h>

using namespace std;

const int maxn=50;
long long f[50];
int main()
{
    f[1]=1,f[2]=2;
    for(int i=3;i<maxn;i++)
    f[i]=f[i-1]+f[i-2];
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        int sign=1;
        for(int i=1;i<maxn;i++)
        {
            if(f[i]>n)
            break;
            if(f[i]==n)
            {
                sign=0;
                break;
            }
        }
        if(sign)
        printf("A\n");
        else
        printf("B\n");
    }
    return 0;
}

威佐夫游戏(精度控制):有2堆石子。A B两个人轮流拿,A先拿。每次可以从一堆中取任意个或从2堆中取相同数量的石子,但不可不取。拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出2堆石子的数量,问最后谁能赢得比赛。


#include <iostream>
#include <cmath>

using namespace std;

typedef unsigned long long ULL;

const ULL Gold[3] = {618033988, 749894848, 204586834};
const ULL MOD = 1000000000;

int main()
{
    int t;
    cin >> t;
    ULL a, b;
    while (t--)
    {
        cin >> a >> b;
        if (a < b)
        {
            swap(a, b);
        }
        ULL dist = a - b;
        ULL pre = dist / MOD, las = dist % MOD;
        ULL a1 = las * Gold[2];
        ULL a2 = pre * Gold[2] + las * Gold[1] + a1 / MOD;
        ULL a3 = pre * Gold[1] + las * Gold[0] + a2 / MOD;
        ULL a4 = dist + pre * Gold[0] + a3 / MOD;
        cout << (a4 == b ? 'B' : 'A') << endl;
    }
}

Nim游戏:有N堆石子。A B两个人轮流拿,A先拿。每次只能从一堆中取若干个,可将一堆全取走,但不可不取,拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出N及每堆石子的数量,问最后谁能赢得比赛。


#include <bits/stdc++.h>

using namespace std;

int main()
{
    int n;
    scanf("%d",&n);
    int ans=0,x;
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&x);
        ans=ans^x;
    }
    if(ans==0)
        printf("B\n");
    else
        printf("A\n");
}

在求一个字符串的最长回文子串时,当数据较大的时候,需要用到Manacher算法:

#include <bits/stdc++.h>
 
using namespace std;
const int N = 220005;
 
char s[N];
int p[N],n,id,mx;
 
void Manacher(){
    id = mx = 0;
    int ans = 0;
    n = strlen(s);
    for(int i = n;i >= 0;i--){
        s[2*i+2] = s[i];
        s[2*i+1] = '#';
    }
    s[0] = '@';
    p[0] = 1;
    for(int i = 1;i < 2*n+2;i++){
        if(mx > i) p[i] = min(p[2*id-i],mx-i);
        else p[i] = 1;
        while(s[i+p[i]] == s[i-p[i]]) p[i] += 1;
        if(mx < p[i]+i){
            id = i;
            mx = p[i]+i;
        }
        ans = max(ans,p[i]-1);
    }
    printf("%d\n",ans);
}
 
int main(){
    while(~scanf("%s",s)) Manacher();
    return 0;
}

给定一个字符串,求插入最少的字符使得原字符串回文,求出该字符串逆序与正序的最长公共子序列长度,再用dp搞一下就行:

#include <bits/stdc++.h>
 
using namespace std;
 
int dp[1005][1005];
 
int main(){
    string x,y;
    cin>>x;
    y = x;
    reverse(y.begin(),y.end());
    for(int i = 0;i < x.length();++i){
        for(int j = 0;j < y.length();++j){
            int p = 0;
            if(x[i] == y[j]) p = 1;
            dp[i+1][j+1] = max(dp[i][j]+p,max(dp[i+1][j],dp[i][j+1]));
        }
    }
    printf("%d\n",x.length()-dp[x.length()][y.length()]);
    return 0;
}

编辑距离既是指给定两个字符串,给定添加,删除,改变3种操作,求定最少的操作次数:

#include <bits/stdc++.h>
 
using namespace std;
 
typedef long long LL;
 
const int inf = 0x3f3f3f3f, N = 1e3 + 5, MOD = 1e9 + 7;
 
int T, cas = 0;
int n, m;
int dp[N][N];
char s[N], t[N];
 
int main(){
    while(scanf("%s%s",s,t) != EOF) {
    	int n = strlen(s), m = strlen(t);
    	for(int i = 0; i <= n; i ++) dp[i][0] = i;
    	for(int i = 0; i <= m; i ++) dp[0][i] = i;
    	for(int i = 1; i <= n; i ++) {
    		for(int j = 1; j <=m; j ++) {
    			dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + 1;
    			dp[i][j] = min(dp[i][j], dp[i-1][j-1] + (s[i-1] != t[j-1]));
    		}
    	}
    	printf("%d\n", dp[n][m]);
    }
    return 0;
}

今年沈阳有个素数计数的题,但是数据到了1e11,大概用Meisell-Lehmer能跑到2、3秒的样子,但是据说还有一种Deleglise Rivat的算法特别快,大概1e16差不多才3、4秒的样子,但是网上没有找到板子,所以就发一个Meisell-Lehmer的模板,1e11内的数差不多都能在5秒内处理完:

#include<bits/stdc++.h>
 
using namespace std;
 
typedef long long LL;
const int N = 5e6 + 2;
bool np[N];
int prime[N], pi[N];
 
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;
}
const int M = 7;
const int PM = 2 * 3 * 5 * 7 * 11 * 13 * 17;
int phi[PM + 1][M + 1], sz[M + 1];
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) {
    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() {
    init();
    LL n;
    while(cin >> n) {
        cout << lehmer_pi(n) << endl;
    }
    return 0;
}

计算模式串在原串中出现的次数

#include <bits/stdc++.h>

using namespace std;
const int maxn = 1e6+10;

int n,Next[maxn];
char mo[maxn],str[maxn];

void getNext(){
    int i = 0,j = -1,len = strlen(mo);
    while(i < len){
        if(j == -1||mo[i] == mo[j]) Next[++i] = ++j;
        else j = Next[j];
    }
}

void kmp(){
    int i = 0,j = 0,len1 = strlen(mo),len2 = strlen(str);
    int ans = 0;
    while(i < len2){
        if(j == -1||mo[j] == str[i]){
            j++;
            i++;
        }
        else j = Next[j];
        if(j == len1) ans++;
    }
    printf("%d\n",ans);
}


int main(){
    int n,m,k;
    while(~scanf("%s%s",mo,str)){
        memset(Next,-1,sizeof(Next));
        getNext();
        kmp();
    }
    return 0;
}

Lucas定理:

Lucas定理是用来求 C(n,m) mod p的值,p是素数(从n取m组合,模上p)。
描述为:
Lucas(n,m,p)=C(n%p,m%p)* Lucas(n/p,m/p,p)
Lucas(x,0,p)=1;
简单的理解就是:

以求解n! % p 为例,把n分段,每p个一段,每一段求得结果是一样的。但是需要单独处理每一段的末尾p,2p,…,把p提取出来,会发现剩下的数正好又是(n/p)! ,相当于

划归了一个子问题,这样递归求解即可。

这个是单独处理n!的情况,当然C(n,m)就是n!/(m! *(n-m)!),每一个阶乘都用上面的方法处理的话,就是Lucas定理了

Lucas最大的数据处理能力是p在10^5左右。

而C(a,b) =a! / ( b! * (a-b)! ) mod p

其实就是求 ( a! / (a-b)!) * ( b! )^(p-2) mod p

(上面这一步变换是根据费马小定理:假如p是质数,且a,p互质,那么a的(p-1)次方除以p的余数恒为1,

那么a和a^(p-2)互为乘法逆元,则(b / a) = (b * a^(p-2) ) mod p)

ll quick_pow(ll a,ll n,ll p)  
{  
    ll x = a;  
    ll res = 1;  
    while(n)  
    {  
        if(n & 1)  
        {  
            res = ((ll)res * (ll)x) % p;  
        }  
        n >>= 1;  
        x = ((ll)x*(ll)x) % p;  
    }  
    return res;  
}  
  
ll C(ll n,ll m,ll p)  
{  
    ll a = 1,b = 1;  
    if(m > n) return 0;  
    //实现(a!/(a-b)!) * (b!)^(p-2)) mod p
    while(m)  
    {  
        a = (a * n) % p;  
        b = (b * m) % p;  
        m--;  
        n--;  
    }  
    return ((ll)a * (ll)quick_pow(b,p-2,p))%p;  
}  
  
ll Lucas(ll n,ll m,ll p)  
{  
    if(m==0)  
        return 1;  
    return((ll)C(n%p,m%p,p)*(ll)Lucas(n/p,m/p,p))%p;  
}  

Dijkstra模板

int cnt, head[maxn]; ll dis[maxn];
struct Edge {
    int v, next; ll w; //记住这里改顺序下面push进队一定要改!
    bool operator < (const Edge &rhs) const {
        return w > rhs.w;
    }
} e[maxn*4];
 
void add(int u, int v, int co) {
    e[cnt].v = v;
    e[cnt].w = co;
    e[cnt].next = head[u];
    head[u] = cnt++;
}
 
void init() {
    cnt = 0;
    memset(head, -1, sizeof(head));
}
 
void dij(int s, int len) {
    priority_queue<Edge> pq;
    for (int i = 1; i <= len; i++)
        dis[i] = inf;
    bool vis[maxn] = {0};
    dis[s] = 0;
    pq.push((Edge){s, 0, 0});
    while (!pq.empty()) {
        Edge tmp = pq.top(); pq.pop();
        if (vis[tmp.v]) continue;
        vis[tmp.v] = 1;
        for (int i = head[tmp.v]; ~i; i = e[i].next) {
            Edge u = e[i];
            if (dis[u.v] > dis[tmp.v] + u.w) {
                dis[u.v] = dis[tmp.v] + u.w;
                pq.push((Edge){u.v, 0, dis[u.v]});
            }
        }
    }
}

带删除01字典树,Query出来的值是字典树中异或出来的最大值

typedef long long ll;
const int maxn = 1e5+10;//数据范围
const int Bit = 32;//数转化成二进制的范围

int ch[maxn*Bit][2],cnt[maxn*Bit];
ll value[maxn*Bit];
int node_cnt;

inline void init(){
    node_cnt = 1;
    memset(ch[0],0,sizeof(ch));
}

inline void Insert(ll x,int f){
    int cur = 0;
    for(int i = 32;i >= 0;i--){
        int idx = (x >> i)&1;
        if(!ch[cur][idx]){
            memset(ch[node_cnt],0,sizeof(ch[node_cnt]));
            ch[cur][idx] = node_cnt;
            cnt[node_cnt] = 0;
            value[node_cnt++] = 0;
        }
        cur = ch[cur][idx];
        cnt[cur] += f;
    }
    value[cur] = x;
}

inline ll Query(ll x){
    int cur = 0;
    for(int i = 32;i >= 0;i--){
        int idx = (x>>i)&1;
        if(ch[cur][idx^1]&&cnt[ch[cur][idx^1]]) cur = ch[cur][idx^1];
        else cur = ch[cur][idx];
    }
    return value[cur]^x;
}

单纯的普通字典树(求前缀用)

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>

using namespace std;
const int maxn = 1e6+10;

int le;
struct node{
    int Next[26];
    int cnt;
    void init(){
        cnt = 0;
        memset(Next,-1,sizeof(Next));
    }
}T[maxn];


void Insert(char *s){
    int i = 0,p = 0;
    while(s[i]){
        int x = s[i]-'a';
        if(T[p].Next[x] == -1){
            T[le].init();
            T[p].Next[x] = le++;
        }
        p = T[p].Next[x];
        T[p].cnt++;
        i++;
    }
}

void query(char *s){
    int i = 0,p = 0;
    while(s[i]){
        int x = s[i]-'a';
        if(T[p].Next[x] == -1){
            puts("0");
            return ;
        }
        p = T[p].Next[x];
        i++;
    }
    printf("%d\n",T[p].cnt);
}
//main函数里面记得先T[0].init();

二分图最大匹配

int line[maxn][maxn],used[maxn],nxt[maxn];//line[i][j]代表有没有边,used代表在这次匹配的过程种右边点用过没有,nxt为右边的归属

bool Find(int x){
    for(int i = 1;i <= m;i++){//m为二分图右边的点个数
        if(line[x][i]&&!used[i]){
            used[i] = 1;
            if(nxt[i] == 0||Find(nxt[i])){
                nxt[i] = x;
                return true;
            }
        }
    }
    return false;
}

int match(){
    int sum = 0;
    for(int i = 1;i <= n;i++){//n为二分图左边的点数,枚举每个点,看能不能匹配
        memset(used,0,sizeof(used));
        if(Find(i)) sum++;
    }
    return sum;
}

米勒拉宾素数测试

ll quick_pow(ll a,ll b,ll r)//快速幂 
{
    ll ans = 1;
    while(b)
    {
        if(b&1)ans=(ans*a)%r;
        a = (a*a)%r;
        b>>=1;
    }
    return ans;
}
bool Miller_Rabbin(int n,int a)//米勒拉宾素数测试 
{
    int r=0,s=n-1,j;
    if(!(n%a))
        return false;
    while(!(s&1)){
        s>>=1;
        r++;
    }
    ll k=qpow(a,s,n);
    if(k==1)
        return true;
    for(j=0;j<r;j++,k=k*k%n)
        if(k==n-1)
            return true;
    return false;
}
bool IsPrime(int n)//判断是否是素数 
{
    int tab[]={2,3,5,7};
    for(int i=0;i<4;i++)
    {
        if(n==tab[i])
            return true;
        if(!Miller_Rabbin(n,tab[i]))
            return false;
    }
    return true;
}

01背包

#include <bits/stdc++.h>

using namespace std;

int main(){
    int n,V,w[1005],dp[1005];
    while(cin>>n&&n){
        memset(dp,0,sizeof(dp));
        for(int i = 1;i <= n;i++)
            cin>>w[i];
        cin>>V;
        sort(w+1,w+1+n);
        if(V < 5) cout<<V<<endl;
        else{
            for(int i = 1;i < n;i++){
                for(int j = V-5;j >= w[i];j--){
                    dp[j] = max(dp[j],dp[j-w[i]]+w[i]);
                }
            }
            cout<<V-dp[V-5]-w[n]<<endl;
        }
    }
    return 0;
}

完全背包(hdoj 1114,完全背包,问能不能装满f-e的重量):

#include <bits/stdc++.h>

using namespace std;

const int INF = 0x3f3f3f3f;
const int maxn = 1e5+10;

int main(){
    int t,e,f,n;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&e,&f);
        scanf("%d",&n);
        int dp[maxn];
        dp[0] = 0;
        for(int i = 1;i < maxn;i++)
            dp[i] = INF;
        for(int i = 0;i < n;i++){
            int weight,value;
            scanf("%d%d",&value,&weight);
            for(int j = weight;j <= f-e;j++)
                dp[j] = min(dp[j],dp[j-weight]+value);
        }
        if(dp[f-e] == INF) printf("This is impossible.\n");
        else printf("The minimum amount of money in the piggy-bank is %d.\n",dp[f-e]);
    }
    return 0;
}

多重背包

#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>

using namespace std;

int t,nValue,nKind,value[105],weight[105],bag[105],dp[105];

int main(){
    scanf("%d",&t);
    while(t--){
        memset(dp,0,sizeof(dp));
        scanf("%d%d",&nValue,&nKind);
        for(int i = 0;i < nKind;i++){
            scanf("%d%d%d",&value[i],&weight[i],&bag[i]);
            for(int j = 0;j < bag[i];j++){
                for(int k = nValue;k >= value[i];k--){
                    dp[k] = max(dp[k],dp[k-value[i]]+weight[i]);
                }
            }
        }
        printf("%d\n",dp[nValue]);
    }
    return 0;
}

prim模板(hihocoder1097,不带优化,时间复杂度为O(n*n)):

int n,mat[maxn][maxn],d[maxn],vis[maxn];
int ans,End;

int main(){
    scanf("%d",&n);
    for(int i = 1;i <= n;i++){
        for(int j = 1;j <= n;j++){
            scanf("%d",&mat[i][j]);
        }
    }
    memset(d,0x3f3f3f,sizeof(d));
    memset(vis,0,sizeof(vis));
    d[1] = 0;
    ans = 0;
    for(int i = 1;i <= n;i++){
        End = -1;
        for(int j = 1;j <= n;j++){
            if(!vis[j]&&(End == -1||d[End] > d[j]))
                End = j;
        }
        ans += d[End];
        vis[End] = 1;
        for(int j = 1;j <= n;j++){
            if(!vis[j]&&d[j] > mat[End][j])
                d[j] = mat[End][j];
        }
    }
    printf("%d\n",ans);
}


kruskal(hihocoder1098,时间复杂度为O(MlogM + M * Ackermann’(M))):

int pre[maxn];

struct edge{
    int from,to,val;
}Edge[maxn];

bool cmp(edge x,edge y){
    return x.val < y.val;
}

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

void Union(int x,int y){
    pre[Find(x)] = Find(y);
}

int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 0;i <= n;i++) pre[i] = i;
    for(int i = 1;i <= m;i++){
        scanf("%d%d%d",&Edge[i].from,&Edge[i].to,&Edge[i].val);
    }
    sort(Edge+1,Edge+1+m,cmp);
    int ans = 0;
    for(int i = 1;i <= m;i++){
        if(Find(Edge[i].from) != Find(Edge[i].to)){
            Union(Edge[i].from,Edge[i].to);
            ans += Edge[i].val;
        }
    }
    printf("%d\n",ans);
    return 0;
}

使用堆优化的prim(hihocoder1109,时间复杂度O(mlogm))

typedef pair<int,int>p;

vector<p >vec[maxn];
int vis[maxn];

int main(){
    int n,m,u,v,val;
    scanf("%d%d",&n,&m);
    memset(vis,0,sizeof(vis));
    for(int i = 1;i <= m;i++){
        scanf("%d%d%d",&u,&v,&val);
        vec[u].push_back(p(val,v));
        vec[v].push_back(p(val,u));
    }
    priority_queue<p,vector<p>,greater<p> >pq;
    vis[1] = 1;
    for(int i = 0;i < vec[1].size();i++){
        pq.push(vec[1][i]);
    }
    int ans = 0;
    while(!pq.empty()){
        p now = pq.top();
        pq.pop();
        if(!vis[now.second]){
            vis[now.second] = 1;
            ans += now.first;
            for(int i = 0;i < vec[now.second].size();i++){
                if(!vis[vec[now.second][i].second])
                    pq.push(vec[now.second][i]);
            }
        }
    }
    printf("%d\n",ans);
}

斯特林近似求N阶乘的长度

#include <bits/stdc++.h>
 
using namespace std;
 
typedef long long ll;
#define PI 3.1415926
#define E  2.718281828459
 
int main()
{
        ll n;
        cin >> n;
        ll res =  0.5 * log10(2.0 * PI * n) + n * log10(n * 1.0 / E) + 1;
        cout << res << endl;
    return 0;
}

Tarjan

//记得每次清边
vector<int>Edge[maxm];
stack<int>S;
int Dfn[maxn],Low[maxn],sccno[maxn],tclock,sccnt;//sccno代表某个点所在的强连通分量编号

void tarjan(int u){
    Dfn[u] = Low[u] = ++tclock;
    S.push(u);

    for(int i = 0;i < Edge[u].size();i++){
        int v = Edge[u][i];
        if(!Dfn[v]){
            tarjan(v);
            Low[u] = min(Low[u],Low[v]);
        }
        else if(!sccno[v]){
            Low[u] = min(Low[u],Dfn[v]);
        }
    }

    if(Low[u] == Dfn[u]){
        sccnt ++;
        int v = -1;
        while(v != u){
            v = S.top();
            S.pop();
            sccno[v] = sccnt;
        }
    }
}

void findscc(int n){
    tclock = sccnt = 0;
    //各种数组初始化
    memset(Dfn,0,sizeof(Dfn));
    memset(Low,0,sizeof(Low));
    memset(sccno,0,sizeof(sccno));
    for(int i = 1;i <= n;i++)
        if(!Dfn[i]) tarjan(i);
}

最大流Dinic:

const int MAXN = 1000+100;
const int MAXM = 1000000+10;
const int INF = 1000000+10;

struct Edge{
    int from, to, cap, flow, next;
}edge[MAXM];

int dist[MAXN], vis[MAXN], cur[MAXN], top, head[MAXN];
int n, m,sumflow,sink;

void init(){
    top = 0;
    memset(head, -1, sizeof(head));
}

void addedge(int u, int v, int w){
    Edge E1 = {u, v, w, 0, head[u]};
    edge[top] = E1;
    head[u] = top++;
    Edge E2 = {v, u, 0, 0, head[v]};
    edge[top] = E2;
    head[v] = top++;
}

bool BFS(int start, int End){
    queue<int> Q;
    memset(dist, -1, sizeof(dist));
    memset(vis, 0, sizeof(vis));
    while(!Q.empty()) Q.pop();
    Q.push(start);
    vis[start] = 1;
    dist[start] = 0;
    while(!Q.empty()){
        int u = Q.front();
        Q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            Edge E = edge[i];
            if(!vis[E.to] && E.cap > E.flow){
                vis[E.to] = 1;
                dist[E.to] = dist[u] + 1;
                if(E.to == End) return true;
                Q.push(E.to);
            }
        }
    }
    return false;
}

int DFS(int x, int a, int End){
    if(x == End || a == 0) return a;
    int flow = 0, f;
    for(int& i = cur[x]; i != -1; i = edge[i].next){
        Edge& E = edge[i];
        if(dist[E.to] == dist[x]+1 && (f = DFS(E.to, min(a, E.cap-E.flow), End)) > 0){
            E.flow += f;
            edge[i^1].flow -= f;
            flow += f;
            a -= f;
            if(a == 0) break;
        }
    }
    return flow;
}

int Dinic(int start, int End){
    int flow = 0;
    while(BFS(start, End)){
        memcpy(cur, head, sizeof(head));
        flow += DFS(start, INF, End);
    }
    return flow;
}

最小费用最大流:

const int INF = 0x3f3f3f3f;
const int MaxNode = 205;
const int MaxEdge = 40005;

struct Edge{
    int to,vol,cost,next;
}Edges[MaxEdge];

int Pre[MaxNode],Path[MaxNode],Dist[MaxNode],Head[MaxNode],EdgeCount;
bool vis[MaxNode];

void addedge(int u, int v, int vol, int cost){
    Edges[EdgeCount].to = v;
    Edges[EdgeCount].vol = vol;
    Edges[EdgeCount].cost = cost;
    Edges[EdgeCount].next = Head[u];
    Head[u] = EdgeCount++;

    Edges[EdgeCount].to = u;
    Edges[EdgeCount].vol = 0;
    Edges[EdgeCount].cost = -cost;
    Edges[EdgeCount].next = Head[v];
    Head[v] = EdgeCount++;
}

bool Spfa(int s, int t){
    memset(Dist, 0x3f3f3f3f, sizeof(Dist));
    memset(Pre, -1, sizeof(Pre));
    memset(vis, false, sizeof(vis));
    Dist[s] = 0;
    queue<int>Q;
    Q.push(s);
    vis[s] = true;
    while (!Q.empty()){
        int u = Q.front();
        Q.pop();
        vis[u] = false;
        for (int e = Head[u]; e != -1; e = Edges[e].next){
            int v = Edges[e].to;
            if (Edges[e].vol > 0 && Dist[v] > Dist[u] + Edges[e].cost){
                Dist[v] = Dist[u] + Edges[e].cost;
                Pre[v] = u;
                Path[v] = e;
                if(!vis[v]){
                    vis[v] = true;
                    Q.push(v);
                }
            }
        }
    }
    return Pre[t] != -1;
}

int MCMF(int s, int t){
    int cost = 0;
    int max_flow = 0;
    int u, v, e;
    while (Spfa(s, t)){
        int f = INF;
        for (u = t; u != s; u = Pre[u]){
            f = min(f, Edges[Path[u]].vol);
        }

        for (u = t; u != s; u = Pre[u]){
            e = Path[u];
            Edges[e].vol -= f;
            Edges[e^1].vol += f;
        }
        max_flow += f;
        cost += f*Dist[t];
    }
    return cost;
}

void init(){
    memset(Head,-1,sizeof(Head));
    EdgeCount = 0;
}

快速幂

ll quick_mod(ll a,ll b,ll mod)//快速幂
{
	ll ans = 1;
	while(b)
	{
		if(b&1)
			ans = (ans*a)%mod;
		a = (a*a)%mod;
		b >>= 1;
	}
	return ans;
}

并查集

int Find(int x){
    int a = x;
    while(x != pre[x]){
        x = pre[x];
    }
    while(a!=pre[x]){
        int z = a;
        a = pre[x];
        pre[x]=x;
    }
    return x;
}
 
void join(int x,int y){
    int p,q;
    p = Find(x),q = Find(y);
    if(p != q) pre[p] = q;
}

int pre[maxn];
 
int Find(int x){
    if(pre[x] == x) return x;
    return pre[x] = Find(pre[x]);
}
 
void join(int x,int y){
    int p,q;
    p = Find(x),q = Find(y);
    if(p != q) pre[p] = q;
}

// 并查集模板
int fa[mn], size[mn];
iota(fa, fa + mn, 0);
fill(size, size + mn, 1);
function<int(int)> find = [&](int x) -> int { return fa[x] == x ? x : fa[x] = find(fa[x]); };
auto merge = [&](int from, int to) {
    from = find(from);
    to = find(to);
    if (from != to) {
        fa[from] = to;
        size[to] += size[from];
    }
};

数位dp

int digit[maxn];
int dp[maxn][][][][];//维度

int dfs(int num,    ,bool limit)   //位数,传递条件(dp维度),上界判断{
    if(num == -1)return  0;  //最后一位时,根据情况返回1或0或者其他情况,看题目到底需要的贡献是个啥
    if(!limit && dp[num][][][](维度)!=-1)      //已经走过此种状态
        return dp[num][][][];
    int ans = 0;      //计数
    int up = limit?a[num]:9;    //上界
    for(int i = 0;i <= up;i++){
        ans += dfs(num-1,    ,limit && i== up);//传递
    }
    if(!limit)  //判断是否可以储存
        dp[num][  ]=ans;
    return ans;
}

int solve(int x)    //将x拆开存入a数组
{
    int num=0;
    while(x){
        digit[num] = x%b; //b表示进制!!!
        num++;
        x/=b;
    }
    return dfs(num-1,   ,true);//传递
}

int main(){
    int l,r;
    while(~scanf("%d%d",&l,&r)){
        memset(dp,-1,sizeof(dp));
        printf("%d\n",solve(r)-solve(l-1));
    }
    return 0;
}

大数加减乘除

#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>

using namespace std;
const int maxn = 1e6+10;

char str1[maxn],str2[maxn],ans[maxn];
string s1,s2;
bool flag1,flag2;
char check;

void Add(char *a,char *b,char *res)
{
    int lena = strlen(a);
    int lenb = strlen(b);
    int i=lena-1,j=lenb-1,k=0;
    while((i+1)||(j+1))
    {
        if(i!=-1)res[k]+=(int)(a[i--]-'0');
        if(j!=-1)res[k]+=(int)(b[j--]-'0');
        res[k+1]+=res[k]/10;
        res[k++]%=10;
    }
    while(!res[k]&&k>1)k--;
    if(res[k])k++;
    res[k]=0;
    for(int s=k-1; s>=0; s--)
        res[s]+='0';
    for(int s=0; s<=(k-1)/2; s++)
        swap(res[s],res[k-1-s]);
}

void Sub(char *a,char *b,char *res)
{
    int lena = strlen(a);
    int lenb = strlen(b);
    int ans[maxn];
    memset(ans,0,sizeof(ans));
    int i=lena-1,j=lenb-1,k=0;
    while((i+1)||(j+1))
    {
        if(j!=-1)ans[k]+=a[i--]-b[j--];
        else ans[k]+=a[i--]-'0';
        if(ans[k]<0)
        {
            ans[k+1]-=1;
            ans[k]+=10;
        }
        k++;
    }
    while(ans[k]==0&&k>=1)k--;
    for(int s=k; s>=0; s--)
        res[s]=ans[s]+'0';
    for(int s=0; s<=k/2; s++)
        swap(res[s],res[k-s]);
    res[k+1]=0;
}

void Mul(char *a,char *b,char *res)
{
    int lena = strlen(a);
    int lenb = strlen(b);
    int k=0;
    for(int i=0;i<lena; i++)
        for(int j=0; j<lenb; j++)
        {
            res[i+j]+=(int)((a[lena-1-i]-'0')*(b[lenb-1-j]-'0'));
            if(res[i+j]>7)
            {
                res[i+j+1]+=res[i+j]/10;
                res[i+j]%=10;
                if(i+j+1>k)k=i+j+1;
            }
            else if(res[i+j]&&i+j>k)k=i+j;
        }
    for(int s=k; s>=0; s--)
        res[s]+='0';
    for(int s=0;s<=k/2;s++)
        swap(res[s],res[k-s]);
}

void init(){
    memset(ans,'\0',sizeof(ans));
    flag1 = flag2 = false;

    if(s1[0] != '-') for(int i = 0;i < s1.size();i++) str1[i] = s1[i];
    else {flag1 = true;for(int i = 1;i < s1.size();i++) str1[i-1] = s1[i];}
    if(s2[0] != '-') {for(int i = 0;i < s2.size();i++) str2[i] = s2[i];}
    else {flag2 = true;for(int i = 1;i < s2.size();i++) str2[i-1] = s2[i];}
}

void judge(){
    int l1 = strlen(str1),l2 = strlen(str2);
    if(l1 < l2) check = '2';
    else if(l1 > l2) check = '1';
    else{
        for(int i = 0;i <= strlen(str1);i++){
            if(str1[i] > str2[i]) {check = '1';return ;}
            if(str1[i] < str2[i]) {check = '2';return ;}
        }
        check = '=';return ;
    }
}


void print(){
    for(int i = 0;i < strlen(ans);i++)
        printf("%c",ans[i]);
    puts("");
}

void ADD(){
    if(flag1&&flag2) {Add(str1,str2,ans);printf("-");print();}//同负
    else if(!flag1&&flag2){//1正2负
        if(check == '1'){Sub(str1,str2,ans);print();}
        else if(check == '2') {Sub(str2,str1,ans);printf("-");print();}
        else puts("0");
    }
    else if(flag1&&!flag2){//1负2正
        if(check == '1'){Sub(str1,str2,ans);printf("-");print();}
        else if(check == '2') {Sub(str2,str1,ans);print();}
        else puts("0");
    }
    else {Add(str1,str2,ans);print();}//同正
}

void SUB(){
    if(flag1&&flag2){//同负
        if(check == '2'){Sub(str2,str1,ans);print();}
        else if(check == '1') {Sub(str1,str2,ans);printf("-");print();}
        else puts("0");
    }
    else if(!flag1&&flag2){Add(str1,str2,ans);print();}//1正2负
    else if(flag1&&!flag2){Add(str1,str2,ans);printf("-");print();}//1负2正
    else {//同正
        if(check == '1'){Sub(str1,str2,ans);print();}
        else if(check == '2') {Sub(str1,str2,ans);printf("-");print();}
        else puts("0");
    }
}

void MUL(){
    if(str1[0] == '0'||str2[0] == '0') puts("0");
    else if(flag1&&flag2){Mul(str1,str2,ans);print();}//同负
    else if(!flag1&&flag2){Mul(str1,str2,ans);printf("-");print();}//1正2负
    else if(flag1&&!flag2){Mul(str1,str2,ans);printf("-");print();}//1负2正
    else{Mul(str1,str2,ans);print();}//同正
}


int main(){
    freopen("2.in","r",stdin);
    freopen("2(1).out","w",stdout);
    while(cin>>s1>>s2){
        init();
        judge();
        //ADD();
        //SUB();
        MUL();
    }
    return 0;
}

线性筛算1到n的欧拉值

void Init(){     
     euler[1]=1;    
     for(int i=2;i<MAXN;i++)    
       euler[i]=i;    
     for(int i=2;i<MAXN;i++)    
        if(euler[i]==i)    
           for(int j=i;j<MAXN;j+=i)    
              euler[j]=euler[j]/i*(i-1);//防止中间数据溢出 
}

筛素数同时筛欧拉值

int tot,pri[MAX],phi[MAXN];
bool mark[MAXN];

void getphi()
{
    phi[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!mark[i]){phi[i]=i-1;pri[++tot]=i;}
        for(int j=1;j<=tot;j++)
        {
            int x=pri[j];
            if(i*x>n)break;
            mark[i*x]=1;
            if(i%x==0){phi[i*x]=phi[i]*x;break;}
            else phi[i*x]=phi[i]*phi[x];
        }
    }
}

树状数组

// arr[i] 为初始数组,下标范围为 [1, n]

for (int i = 1; i <= n; ++i)  // step 1
  tmp[i] = arr[i];
sort(tmp + 1, tmp + n + 1);                          // step 2
int len = unique(tmp + 1, tmp + n + 1) - (tmp + 1);  // step 3
for (int i = 1; i <= n; ++i)                              // step 4
  arr[i] = lower_bound(tmp + 1, tmp + len + 1, arr[i]) - tmp;

vector<int> tmp(arr);  // tmp 是 arr 的一个副本
sort(tmp.begin(), tmp.end());
tmp.erase(unique(tmp.begin(), tmp.end()), tmp.end());
for (int i = 0; i < n; ++i)
  arr[i] = lower_bound(tmp.begin(), tmp.end(), arr[i]) - tmp.begin();
  
1)、单点增减+区间求和
思路:C[x]表示该点的元素:sum(x)=C[1]+C[2]+……C[x]
int arr[MAXN];
int lowbit(int x) { return x & (-x); }

inline int sum(int x){int res=0; while(x) res+=arr[x],x-=lowbit(x);return res;}

inline void add(int x,int n){while(x<MAXN) arr[x]+=n,x+=lowbit(x);}

inline int query(int x,int y){return sum(y)-sum(x-1);}2)、区间增减+单点查询
思路:C[x]表示该点元素与左边元素的差值:num[x]=C[1]+C[2]+……C[x]
int arr[MAXN];

int lowbit(int x) { return x & (-x); }

inline int sum(int x){int res=0; while(x) res+=arr[x],x-=lowbit(x);return res;}

inline void add(int x,int n){while(x<MAXN) arr[x]+=n,x+=lowbit(x);}

inline int update(int x,int y,int n){add(x,n); add(y+1,-n);}

莫比乌斯函数

ll prime[maxn],mob[maxn],vis[maxn],cnt;

void Mobius(){
    memset(prime,0,sizeof(prime));
    memset(mob,0,sizeof(mob));
    memset(vis,0,sizeof(vis));
    mob[1] = 1;
    cnt = 0;
    for(ll i = 2;i < maxn; i++){
        if(!vis[i]){
            prime[cnt++] = i;
            mob[i] = -1;
        }
        for(ll j = 0;j < cnt&&i*prime[j] < maxn;j++){
            vis[i*prime[j]] = 1;
            if(i%prime[j]) mob[i*prime[j]] = -mob[i];
            else{
                mob[i*prime[j]] = 0;
                break;
            }
        }
    }
}

矩阵快速幂

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
 
using namespace std;
typedef long long ll;
const ll maxn = ;//矩阵单位
const ll MOD = 1e9+7;
#define mod(x) ((x)%MOD)
 
struct mat{
    ll m[maxn][maxn];
    /*
    void Clear(){memset(m,0,sizeof(m));}
    void unit(){
        Clear();
        for(int i = 0;i <= maxn-1;i++) m[i][i] = 1;
    }//有时候可能会用到去简化处理
    */
}unit;
 
mat operator *(mat a,mat b){
    mat ret;
    ll x;
    for(ll i = 0;i < maxn;i++){
        for(ll j = 0;j < maxn;j++){
            x = 0;
            for(ll k = 0;k < maxn;k++)
                x += mod((ll)a.m[i][k]*b.m[k][j]);
            ret.m[i][j] = mod(x);
        }
    }
    return ret;
}//普通处理
 
/*
mat operator *(mat a,mat b){
    mat ret;
    for(ll i = 0;i < maxn;i++)
        for(ll j = 0;j < maxn;j++)
            ret.m[i][j] = 0;
    for(ll i = 0;i < maxn;i++){
        for(ll k = 0;k < maxn;k++){
            if(a.m[i][k]){
                for(ll j = 0;j < maxn;j++)
                    ret.m[i][j] = mod(ret.m[i][j] + mod(a.m[i][k]*b.m[k][j]));
            }
        }
    }
    return ret;
}稀疏矩阵剪枝处理
*/
 
void init_unit(){
    for(ll i = 0;i < maxn;i++)
        unit.m[i][i] = 1;
    return ;
}
 
mat pow_mat(mat a,ll n){
    mat ret = unit;
    while(n){
        if(n&1) ret = ret*a;
        a = a*a;
        n >>= 1;
    }
    return ret;
}
 
int main(){
    ll N,M,x;
    init_unit();
    while(~scanf("%lld%lld",&N,&M)){
 
        mat a,b;
        //对a,b矩阵进行初始化的处理
        b = pow_mat(b,M);//多少次幂由题意决定
        a = a*b;
        printf("%lld\n",(a.m[0][0]+MOD)%MOD);//输出由题意决定
 
 
    }
    return 0;
}

RMQ算法,主要用于求区间最大值或者区间最小值
本质就是通过倍增的方法进行计算


int dp[10005][20];
int A[10005][20];

void ST(int n) {
	//ST算法构造dp
    for (int i = 1; i <= n; i++)
        dp[i][0] = A[i];
    for (int j = 1; (1 << j) <= n; j++) {
        for (int i = 1; i + (1 << j) - 1 <= n; i++) {
            dp[i][j] = max(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
        }
    }
}
int RMQ(int l, int r) {
	//RMQ查询
    int k = 0;
    while ((1 << (k + 1)) <= r - l + 1) k++;
    return max(dp[l][k], dp[r - (1 << k) + 1][k]);


数位dp

class Solution {
public:
    long long numberOfPowerfulInt(long long start, long long finish, int limit, string s) {
        string low = to_string(start);
        string high = to_string(finish);
        int n = high.size();
        low = string(n - low.size(), '0') + low; // 补前导零,和 high 对齐
        int diff = n - s.size();

        vector<long long> memo(n, -1);
        function<long long(int, bool, bool)> dfs = [&](int i, bool limit_low, bool limit_high) -> long long {
            if (i == low.size()) {
                return 1;
            }

            if (!limit_low && !limit_high && memo[i] != -1) {
                return memo[i]; // 之前计算过
            }

            // 第 i 个数位可以从 lo 枚举到 hi
            // 如果对数位还有其它约束,应当只在下面的 for 循环做限制,不应修改 lo 或 hi
            int lo = limit_low ? low[i] - '0' : 0;
            int hi = limit_high ? high[i] - '0' : 9;

            long long res = 0;
            if (i < diff) { // 枚举这个数位填什么
                for (int d = lo; d <= min(hi, limit); d++) {
                    res += dfs(i + 1, limit_low && d == lo, limit_high && d == hi);
                }
            } else { // 这个数位只能填 s[i-diff]
                int x = s[i - diff] - '0';
                if (lo <= x && x <= min(hi, limit)) {
                    res = dfs(i + 1, limit_low && x == lo, limit_high && x == hi);
                }
            }

            if (!limit_low && !limit_high) {
                memo[i] = res; // 记忆化 (i,false,false)
            }
            return res;
        };
        return dfs(0, true, true);
    }
};

作者:灵茶山艾府
链接:https://leetcode.cn/problems/count-the-number-of-powerful-integers/solutions/2595149/shu-wei-dp-shang-xia-jie-mo-ban-fu-ti-da-h6ci/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
作者:灵茶山艾府

自然溢出hash

#include <iostream>

using namespace std;

typedef unsigned long long ULL;

const int N = 1e5 + 10, P = 131;
ULL h[N], p[N];
char s[N];
int n, m;

ULL sub_hash(int l, int r) {
    return h[r] - h[l - 1] * p[r - l + 1];
}

int main() {
    scanf("%d%d\n%s\n", &n, &m, s + 1);
    p[0] = 1L;
    for (int i = 1; i <= n; ++i) {
        h[i] = h[i - 1] * P + s[i];
        p[i] = p[i - 1] * P;
    }
    while (m--) {
        int l1, r1, l2, r2;
        scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
        puts(sub_hash(l1, r1) == sub_hash(l2, r2) ? "Yes" : "No");
    }

    return 0;
}
作者:GabrielxD
链接:https://www.acwing.com/solution/content/148484/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

双值hash

namespace utils {

static int D = 127;
static int mod1 = 1e9 + 7, mod2 = 1e9 + 9;
vector<long long> pow1, pow2;
void init(int l) {
  pow1.resize(l + 1, 0);
  pow1[0] = 1; pow2 = pow1;
  for (int i = 1; i <= l; i ++)
    pow1[i] = pow1[i - 1] * D % mod1,
    pow2[i] = pow2[i - 1] * D % mod2;
}
int qmod(int v, int m) {
  return v + ((v >> 31) & m);
}
struct Hash {
  vector<long long> val1, val2;
  Hash(string s) {
    val1.reserve(s.size());
    val2.reserve(s.size());
    long long v1 = 0, v2 = 0;
    for (auto c: s) {
      v1 = (v1 * D + c) % mod1;
      v2 = (v2 * D + c) % mod2;
      val1.push_back(v1);
      val2.push_back(v2);
    }
  }
  pair<int, int> val() {
    return {val1.back(), val2.back()};
  }
  pair<int, int> val(int l, int r) {
    if (l == 0)
      return {val1[r], val2[r]};
    return {
      qmod(val1[r] - val1[l - 1] * pow1[r - l + 1] % mod1, mod1),
      qmod(val2[r] - val2[l - 1] * pow2[r - l + 1] % mod2, mod2)
    };
  }
};

}

using utils::Hash;
using utils::init;
pair<int, int> H(string s) {
  return Hash(s).val();
}

class Solution {
public:
    long long countPrefixSuffixPairs(vector<string>& words) {
        init(1e5);
        map<pair<int, int>, int> mp;
        long long ans = 0;
        for (int i = 0; i < (int)words.size(); i ++) {
            auto h = Hash(words[i]);
            for (int l = 1; l <= (int)words[i].size(); l ++) {
                if (h.val(0, l - 1) == h.val(words[i].size() - l, words[i].size() - 1))
                    ans += mp[h.val(0, l - 1)];
            }
            ++ mp[h.val()];
        }
        return ans;
    }
};

后缀数组

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

const int N = 1000010;

char s[N];
int n;
int m, p, rk[N * 2], oldrk[N], sa[N * 2], id[N], cnt[N];

int main() {
  scanf("%s", s + 1);
  n = strlen(s + 1);
  m = 128;

  for (int i = 1; i <= n; i++) cnt[rk[i] = s[i]]++;
  for (int i = 1; i <= m; i++) cnt[i] += cnt[i - 1];
  for (int i = n; i >= 1; i--) sa[cnt[rk[i]]--] = i;

  for (int w = 1;; w <<= 1, m = p) {  // m = p 即为值域优化
    int cur = 0;
    for (int i = n - w + 1; i <= n; i++) id[++cur] = i;
    for (int i = 1; i <= n; i++)
      if (sa[i] > w) id[++cur] = sa[i] - w;

    memset(cnt, 0, sizeof(cnt));
    for (int i = 1; i <= n; i++) cnt[rk[i]]++;
    for (int i = 1; i <= m; i++) cnt[i] += cnt[i - 1];
    for (int i = n; i >= 1; i--) sa[cnt[rk[id[i]]]--] = id[i];

    p = 0;
    memcpy(oldrk, rk, sizeof(oldrk));
    for (int i = 1; i <= n; i++) {
      if (oldrk[sa[i]] == oldrk[sa[i - 1]] &&
          oldrk[sa[i] + w] == oldrk[sa[i - 1] + w])
        rk[sa[i]] = p;
      else
        rk[sa[i]] = ++p;
    }

    if (p == n) break;  // p = n 时无需再排序
  }

  for (int i = 1; i <= n; i++) printf("%d ", sa[i]);

  return 0;
}

二维前缀和

//构造前缀和
for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
            sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j];
//求前缀和
int getSum(vector<int> sum, int i, int j, int m, int n) {
    return sum[m][n] - sum[m][j - 1] - sum[i - 1][n] + sum[i - 1][j - 1];
}


二维差分

//二维区间加减,构造差分,再套一层二维前缀和就可以拿到差分后的原数组
void insert(int x1, int y1, int x2, int y2, int c)
{
    b[x1][y1] += c;
    b[x2 + 1][y1] -= c;
    b[x1][y2 + 1] -= c;
    b[x2 + 1][y2 + 1] += c;
}

  

sg函数

//f[N]:可改变当前状态的方式,N为方式的种类,f[N]要在getSG之前先预处理
//SG[]:0~n的SG函数值
//S[]:为x后继状态的集合
int f[N],SG[MAXN],S[MAXN];
void  getSG(int n){
    int i,j;
    memset(SG,0,sizeof(SG));
    //因为SG[0]始终等于0,所以i从1开始
    for(i = 1; i <= n; i++){
        //每一次都要将上一状态 的 后继集合 重置
        memset(S,0,sizeof(S));
        for(j = 0; f[j] <= i && j <= N; j++)
            S[SG[i-f[j]]] = 1;  //将后继状态的SG函数值进行标记
        for(j = 0;; j++) if(!S[j]){   //查询当前后继状态SG值中最小的非零值
            SG[i] = j;
            break;
        }
    }
}


int mex(auto v) {  // v可以是vector、set等容器
    unordered_set<int> S;
    for (auto e : v) S.insert(e);
    for (int i = 0;; ++i)
        if (S.find(i) == S.end()) return i;
}

主席树

#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1e5;  // 数据范围
int tot, n, m;
int sum[(maxn << 5) + 10], rt[maxn + 10], ls[(maxn << 5) + 10],
    rs[(maxn << 5) + 10];
int a[maxn + 10], ind[maxn + 10], len;

int getid(const int &val) {  // 离散化
  return lower_bound(ind + 1, ind + len + 1, val) - ind;
}

int build(int l, int r) {  // 建树
  int root = ++tot;
  if (l == r) return root;
  int mid = l + r >> 1;
  ls[root] = build(l, mid);
  rs[root] = build(mid + 1, r);
  return root;  // 返回该子树的根节点
}

int update(int k, int l, int r, int root) {  // 插入操作
  int dir = ++tot;
  ls[dir] = ls[root], rs[dir] = rs[root], sum[dir] = sum[root] + 1;
  if (l == r) return dir;
  int mid = l + r >> 1;
  if (k <= mid)
    ls[dir] = update(k, l, mid, ls[dir]);
  else
    rs[dir] = update(k, mid + 1, r, rs[dir]);
  return dir;
}

int query(int u, int v, int l, int r, int k) {  // 查询操作
  int mid = l + r >> 1,
      x = sum[ls[v]] - sum[ls[u]];  // 通过区间减法得到左儿子中所存储的数值个数
  if (l == r) return l;
  if (k <= x)  // 若 k 小于等于 x ,则说明第 k 小的数字存储在在左儿子中
    return query(ls[u], ls[v], l, mid, k);
  else  // 否则说明在右儿子中
    return query(rs[u], rs[v], mid + 1, r, k - x);
}

void init() {
  scanf("%d%d", &n, &m);
  for (int i = 1; i <= n; ++i) scanf("%d", a + i);
  memcpy(ind, a, sizeof ind);
  sort(ind + 1, ind + n + 1);
  len = unique(ind + 1, ind + n + 1) - ind - 1;
  rt[0] = build(1, len);
  for (int i = 1; i <= n; ++i) rt[i] = update(getid(a[i]), 1, len, rt[i - 1]);
}

int l, r, k;

void work() {
  while (m--) {
    scanf("%d%d%d", &l, &r, &k);
    printf("%d\n", ind[query(rt[l - 1], rt[r], 1, len, k)]);  // 回答询问
  }
}

int main() {
  init();
  work();
  return 0;
}

my.cpp

#include<iostream>
using namespace std;
typedef long long ll;
//不开longlong见祖宗!

int main(){
    freopen("test.in","r",stdin);
    freopen("test1.out","w",stdout);
		int n;
    cin>>n;
    cout<<n * (n + 1) / 2<<endl;
    return 0;
}

std.cpp

#include<iostream>
using namespace std;
typedef long long ll;
//不开longlong见祖宗!

int main(){
    freopen("test.in","r",stdin);
    freopen("test2.out","w",stdout);
		int n;
    ll sum = 0;
    cin>>n;
	  for(int i = 1; i <= n; ++i)sum += i;
  	cout<<sum<<endl;
    return 0;
}

data.cpp

#include<iostream>
using namespace std;

int main()
{
    freopen("test.in","w",stdout);
    srand(time(0));
    int a = rand() % 100000;
    cout<<a<<endl;
    return 0;
}

duipai.cpp

#include<cstdio>
#include<cstdlib>
#include<iostream>

int main()
{
    int tmp=0;
    for(int i=1;i<=10000;i++)
    {
        system("./data");
        system("./my");
        system("./std");

        // if(i/100>tmp) {printf("-- %d --\n",i);tmp=i/100;}
        if(system("diff test1.out test2.out"))
        {
            printf("wrong in --> %d \n",i);
            break;
        }
        printf("process %d is correct\n",i);
    }
    return 0;
}



  • 14
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值