一点板子

快读、关同步

int read(){
    int f=1,x=0;char c=getchar();
    while(!isdigit(c)) {
          if(c=='-')f=-1;
          c=getchar();
    }
    while(isdigit(c)){
          x=x*10+c-'0';
          c=getchar();
    }
    return x*f;
}


ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);


二进制转十进制

int get_10(string s){
      int val=0;
      for(int i=0;i<s.size();i++){
          val*=2;
          val+=s[i]-'0';
      }
      return val;
}

二进制拆分

int n;
v[N],w[N],dp[N];
int cnt;
for(int i=1;i<=n;i<<=1){
    v[++cnt]=a*j;
    w[cnt]=b*j;
    c-=j;
}
if(n)v[++cnt]=a*c,w[cnt]=b*c;

子段最大异或和问题Trie树(前缀树、字典树)


const int N = 1000050;
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];
}

欧拉筛

       
        int d=0;
	int p[100010]={0};
	int f[100010]={1,1};
	int n;
	cin>>n;
	for(int i=2;i<=n;i++){
		if(f[i]==0){//如果没被标记过,那么i是质数 
			p[d++]=i;
		}
		for(int j=0;j<d;j++){
			if(p[j]*i<=n){//标记以i为最大因数的数为不是素数(除了1和本身) 
				f[p[j]*i]=1;
			}else{
				break;
			}
			if(i%p[j]==0){//如果p[j]是i的因数,那么后面的数都不是以i为最大因数的 
				break;
			}
		}
	}
	
	for(int i=0;i<d;i++){//打印1到n的质数 
		cout<<p[i]<<' ';
	}

树状数组

const int N = 1000;
#define lowbit(x)  ((x) & - (x))
int tree[N]={0};
void update(int x, int d) {   //修改元素a[x],  a[x] = a[x] + d
    while(x <= N) {
        tree[x] += d;
        x += lowbit(x);
    }
}
int sum(int x) {          //返回前缀和sum = a[1] + a[2] +... + a[x]
    int ans = 0;
    while(x > 0){
        ans += tree[x];
        x -= lowbit(x);
    }
    return ans;
}


线段树

inline void build(int i,int l,int r){//递归建树
    tree[i].l=l;tree[i].r=r;
    if(l==r){//如果这个节点是叶子节点
        tree[i].sum=input[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(i*2,l,mid);//分别构造左子树和右子树
    build(i*2+1,mid+1,r);
    tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;//刚才我们发现的性质return ;
}



inline int search(int i,int l,int r){
    if(tree[i].l>=l && tree[i].r<=r)//如果这个区间被完全包括在目标区间里面,直接返回这个区间的值
        return tree[i].sum;
    if(tree[i].r<l || tree[i].l>r)  return 0;//如果这个区间和目标区间毫不相干,返回0
    int s=0;
    if(tree[i*2].r>=l)  s+=search(i*2,l,r);//如果这个区间的左儿子和目标区间又交集,那么搜索左儿子
    if(tree[i*2+1].l<=r)  s+=search(i*2+1,l,r);//如果这个区间的右儿子和目标区间又交集,那么搜索右儿子
    return s;
}


inline void add(int i,int dis,int k){
    if(tree[i].l==tree[i].r){//如果是叶子节点,那么说明找到了
        tree[i].sum+=k;
        return ;
    }
    if(dis<=tree[i*2].r)  add(i*2,dis,k);//在哪往哪跑
    else  add(i*2+1,dis,k);
    tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;//返回更新
    return ;
}

区间修改:
void modify(int p, int l, int r, int k) 
{
	if(tr[p].l >= l && tr[p].r <= r) {
		tr[p].num += k;
		return ;
	}
	int mid = tr[p].l + tr[p].r >> 1;
	if(l <= mid) modify(p << 1, l, r, k);
	if(r > mid) modify(p << 1 | 1, l, r, k);
}



void query(int p, int x)
{
	ans += tr[p].num;//一路加起来
	if(tr[p].l == tr[p].r) return;
	int mid = tr[p].l + tr[p].r >> 1;
	if(x <= mid) query(p << 1, x);
	else query(p << 1 | 1, x); 
}

区间修改,区间查询:

void add(int i,int l,int r,int k)
{
    if(tree[i].r<=r && tree[i].l>=l)//如果当前区间被完全覆盖在目标区间里,讲这个区间的sum+k*(tree[i].r-tree[i].l+1)
    {
        tree[i].sum+=k*(tree[i].r-tree[i].l+1);
        tree[i].lz+=k;//记录lazytage
        return ;
    }
    push_down(i);//向下传递
    if(tree[i*2].r>=l)
        add(i*2,l,r,k);
    if(tree[i*2+1].l<=r)
        add(i*2+1,l,r,k);
    tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
    return ;
}


void push_down(int i)
{
    if(tree[i].lz!=0)
    {
        tree[i*2].lz+=tree[i].lz;//左右儿子分别加上父亲的lz
        tree[i*2+1].lz+=tree[i].lz;
        int mid=(tree[i].l+tree[i].r)/2;
        tree[i*2].sum+=tree[i].lz*(mid-tree[i*2].l+1);//左右分别求和加起来
        tree[i*2+1].sum+=tree[i].lz*(tree[i*2+1].r-mid);
        tree[i].lz=0;//父亲lz归零
    }
    return ;
}



inline int search(int i,int l,int r){
    if(tree[i].l>=l && tree[i].r<=r)
        return tree[i].sum;
    if(tree[i].r<l || tree[i].l>r)  return 0;
    push_down(i);
    int s=0;
    if(tree[i*2].r>=l)  s+=search(i*2,l,r);
    if(tree[i*2+1].l<=r)  s+=search(i*2+1,l,r);
    return s;
}

组合数

ll fac[110],inv[110];
const ll mod=998244353;
ll power(ll x,ll y){
    ll ans=1;
    while(y){
        if(y&1)ans=ans*x%mod;
        x=x*x%mod;
        y>>=1;
    }
    return ans;
}
void init(){
    fac[0]=1;inv[0]=power(1,mod-2);
    for(ll i=1;i<=100;i++){
        fac[i]=i*fac[i-1]%mod;inv[i]= power(fac[i],mod-2);
    }
}
ll C(ll x,ll y){
    if(x<y)return 0;
    return fac[x]*inv[y]%mod*inv[x-y]%mod;
}

另一个线段树板子

#include<bits/stdc++.h>
using namespace std;
const int inf=2e9;
const int iinf=-1;
struct node{
	int minn,maxx;
}tr[400005];
int ql,qr,smn,smx;
int n,k;
int ans[100005][2];
void build(int o,int l,int r){
	tr[o].maxx=iinf;
	tr[o].minn=inf;
	if(l==r){
		int x;
		scanf("%d",&x);
		tr[o].maxx=tr[o].minn=x;
		return;
	}
	int m=(l+r)>>1;
	build(o*2,l,m);
	build(o*2+1,m+1,r);
	tr[o].maxx=max(tr[o*2].maxx,tr[o*2+1].maxx);
	tr[o].minn=min(tr[o*2].minn,tr[o*2+1].minn);
}
void qu(int o,int l,int r){
	if(ql<=l&&qr>=r){
		smn=min(smn,tr[o].minn);
		smx=max(smx,tr[o].maxx);
		return;
	}
	int m=(l+r)>>1;
	if(qr<=m)
		qu(o*2,l,m);
	else if(ql>m)
		qu(o*2+1,m+1,r);
	else{
		qu(o*2,l,m);
		qu(o*2+1,m+1,r);
	}
}
int check(int l){
	for(int i=1;i+l-1<=n;i++){
		smn=inf;
		smx = -1;
		ql = i;
		qr = i+l-1;
		qu(1,1,n);
		if(smx-smn<=k)
			return 1;
	}
	return 0;
}
int main(){
	ios::sync_with_stdio(false);
	scanf("%d%d",&n,&k);
	build(1,1,n);
	int l=1,r=n,len=-1;
	while(l<=r){
		int m=(l+r)>>1;
		if(check(m)){
			l=m+1;
			len=max(len,m);
		}
		else r=m-1;
	}
	int cnt=0;
	for(int i=1;i+len-1<=n;i++){
		smn=inf;
		smx =-1;
		ql=i;
		qr = i+len-1;
		qu(1,1,n);
		if(smx-smn <= k){
			ans[cnt][0]=i;
			ans[cnt++][1]=i+len-1;
		}
	}
	cout<<len<<" "<<cnt<<"\n";
	for(int i=0;i<cnt;i++)
		cout<<ans[i][0]<<" "<<ans[i][1]<<"\n";
	return 0;
}

拓展欧几里得

定理1 gcd(a,b)是ax+by的线性组合的最小正整数,x,y∈z;  定理2 如果ax+by=c,x,y∈z;则c%gcd==0;  定理3 如果a,b是互质的正整数,c是整数,且方程
  ax+by=c代码如下:

int exgcd(int a , int b , int &x , int &y) {
    int res = a;
    if(!b) {
        x = 1 , y = 0;
    }
    else {
        res = exgcd(b , a % b , x , y);
        int temp = x;
        x = y;
        y = temp - a / b * y;
    }
    return res;
}

数论——反素数

我们知道任意一个正数都能被分解为多个素数相乘的形式,即素因数分解。我们又知道一个数的约数的个数等于各素因数的指数加一的乘积。

求有n个因数的最小正整数:

int prim[16]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};//定义16个是因为这16个想乘正好超ull
int n;
uLL ans;
void dfs(int pos,uLL v,int num){//注意v的类型。。。
if(num==n&&ans>v)ans=v;
//if(num>n)return;
for(int i=1;i<=63;i++){
if(num*(i+1)>n||v*prim[pos]>ans)break;//这里在于优化,要不要均可以;
dfs(pos+1,v*=prim[pos],num*(i+1));//保证小的素数必须成才能保证值的小。。。
}
}

字符串哈希

h[i]=h[i-1]p+s[i]-‘a’+1;
w[h-r]=h[h]-h[r-1]
q ^ (h-r+1);
代码:

#include<stdio.h>
#include<string.h>
char a[1000010];
unsigned long long h[1000010],q[1000010];//一定要注意数据类型
int main()
{
    int base=131;//将p取为131
    scanf("%s",a+1);
    int n,m,i;
    m=strlen(a+1);
    h[0]=0;
    q[0]=1;
    for(i=1;i<=m;i++)//从1开始
    {
        h[i]=h[i-1]*base+(a[i]-'a'+1);
        q[i]=q[i-1]*base;
    }

拓扑序

有向无环图 DAG
bfs模板

while(q.size()){
      int x=q.top();  q.pop();
      ans.push_back(x);  //判环
      cout<<x<<'  ';
      for(int i=0;i<v[x].size();i++){
            int y=v[x][i];
            in[y]--;
            if(!in[y])q.push(y);
      }
}
if(ans.size()==n) 无环,有拓扑序
else 有环,无拓扑序

最短路径——Floyd

求各顶点间距离的一种算法,
可以用于无向图和有向图中。
也可以用于负权的最短路径问题(虽然复杂度会比较高)。
Floyd算法的时间复杂度为O(n3),空间复杂度为O(n2)。

for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
    if(i!=j){
          f[i][j]=0x3f3f3f3f;
}
}
}

for(int k=1;k<=n;k++){
  for(int i=1;i<=n;i++){
     for(int j=1;j<=n;j++){
        f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
      }
   }
}

最短路径之——Bellman-ford

求解带负权边的单源最短路问题的经典算法。时间复杂度是O(nm)
一般用于实现通过m次迭代求出从起点到终点不超过m条边构成的最短路径。
注意如果图中有负权回路的话,最短路就不一定存在了

int bellman_ford()
{
    //初始化距离
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    for(int i=0;i<k;i++)
    {
        //记得备份,不然会发生串联
        memcpy(backup,dist,sizeof dist);
        //更新所有的边
        for(int j=0;j<m;j++)
        {
            int a=edg[j].a,b=edg[j].b,c=edg[j].c;
            dist[b]=min(dist[b],backup[a]+c);
        }
    }
    return dist[n];
}

//这里不用0x3f3f3f3f是因为防止n号点被负权边更新
if(bellman_ford()>0x3f3f3f3f/2)

最短路之——Dijkstra

边权不能是负数
堆优化版 的Dijkstra算 法的时间复杂度是O(mlogn),适合于稀疏图


int dist[N];
bool st[N];
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> q;
void dij(){
    memset(dist,inf,sizeof(dist));
    dist[s]=0;
    q.push({0,s});
    while(q.size()){
        auto k=q.top();q.pop();
        int ver=k.second,distance=k.first;
        if(st[ver])continue;
        st[ver]=true;
        for(int i=0;i<g[ver].size();i++){
            int j=g[ver][i].first;
            if(dist[j]>distance+g[ver][i].second){
                dist[j]=distance+g[ver][i].second;
                q.push({dist[j],j});
            }
        }
    }
}

最短路之——spfa

void spfa(){
    queue<int> q;
    q.push(1);
    dis[1] = 0;
    st[1] = true; 
    while(!q.empty() ){
    	//先初始化放入初始源点
        int u = q.front();
        q.pop();
        st[u] = false;
        
        //将此点优化过的点再次放入队列,重复这个过程直至队列为空即可
        for(int i = h[u]; ~ i; i = ne[i] ){
            int j = e[i];
            if(dis[j] > dis[u] + w[i] ){
                dis[j] = dis[u] + w[i];
                // cnt[j] = cnt[u] + 1;
                // if (cnt[j] >= n) puts("存在负环")
                if(!st[j]){
                    q.push(j);
                    st[j] = true;
                    
                }
            }
        }
    }
    
}

二进制模板


while (l <= r) {
            mid = (l + r) >> 1;
            if (f(mid) <= 0) res = mid ,r = mid - 1;              else l = mid + 1;
            }

二分图

二分图定义:一定不含有奇数环,可能包含长度为偶数的环,不一定是连通图,且将所有点分成两个集合,所有边只出现在集合之间,就是二分图

判定代码:

int n = graph.size();
		vector<int> cols(n, 0);
		queue<int> q;
		
		for(int i = 0; i < n; i++) {
			
			if (!cols[i]) {
				q.push(i);
				cols[i] = 1;
				
				while (!q.empty()) {
					int node = q.front();
					q.pop();
					for(int j : graph[node]) {
						int temp = cols[node];
						if (temp == cols[j]) return false;
						if (!cols[j]) {
							cols[j] = -temp;
							q.push(j);
						}
					}
				}
				
			}
			
		}
		
		return true;
    }


最小生成树——prim

复杂度O(n方)
加点,n个点

void prim(){
    memset(dist,inf,sizeof(dist));
    dist[1]=0;vis[1]=true;
    for(int i=0;i<g[1].size();i++)dist[g[1][i].first]=min(g[1][i].second,dist[g[1][i].first]);
    for(int i=2;i<=n;i++){
        int temp=inf,t=-1;
        for(int j=2;j<=n;j++){
            if(!vis[j]&&dist[j]<temp){
                temp=dist[j];t=j;
            }
        }
        if(t==-1){
            res=inf;return ;
        }
        vis[t]=true;res+=dist[t];
        for(int j=0;j<g[t].size();j++){
            dist[g[t][j].first]=min(dist[g[t][j].first],g[t][j].second);
        }
    }
}

最小生成树——Kruskal

加边,n-1条边
并查集

int father[maxn];//father数组存放的是父亲结点
struct Edge{
	int u;//左端点
	int v;//右端点
	int dis;//权值
}ed[maxn];
bool cmp(Edge a,Edge b){//结构体排序
	return a.dis < b.dis;
}
void init(int n){//初始化
	for(int i=1;i<=n;i++) father[i] = i;
}
int find_father(int x){//并查集核心操作
	if(x==father[x]) return x;
	int temp = find_father(father[x]);//路径压缩
	return temp;
}
int Kruskal(int n,int m){
	int ans = 0;//记入最小生成树的权值
	int num_Edge = 0;//边的数目
	for(int i=0;i<m;i++){
		int fu = find_father(ed[i].u);//找最终父亲
		int fv = find_father(ed[i].v);//找最终父亲
		if(fu!=fv){
			father[fu] = fv;//合并
			ans+=ed[i].dis;
			num_Edge++;
			if(num_Edge==n-1) break;
		}
	}
	if(num_Edge!=n-1) return -1;//退出条件
	else return ans;
}

欧拉路

O(n+m)
欧拉路径(欧拉通路):通过图中所有边的简单路。(换句话说,每条边都通过且仅通过一次)也叫”一笔画”问题。
  欧拉回路:闭合的欧拉路径。(即一个环,保证每条边都通过且仅通过一次)

1.对于无向图,所有边都是连通的。
(1) 存在欧拉路径的充分必要条件: 度数为奇数的点只能有0或2个。
(2) 存在欧拉回路的充分必要条件: 度数为奇数的点只能有0个。
2.对于有向图,所有边都是连通.
(1) 存在欧拉路径的充分必要条件: 要么所有点的出度均等于入度要么除了两个点之外,其余所有点的出度等于入度,剩余的两个点: 一个满足出度比入度多1 (起点) ,另一个满足入度比出度多1 (终点)
(2) 存在欧拉回路的充分必要条件: 所有点的出度均等于入度

具有欧拉回路的图叫欧拉图
只具备通路无回路的图叫半欧拉图

#include <bits/stdc++.h>
using namespace std;
const int MAX=100010;
int n,m,u,v,del[MAX];
int du[MAX][2];//记录入度和出度 
stack <int> st;
vector <int> G[MAX];
void dfs(int now)
{
	for(int i=del[now];i<G[now].size();i=del[now])
	{ 
		del[now]=i+1;
		dfs(G[now][i]);
	}
	st.push(now);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) scanf("%d%d",&u,&v),G[u].push_back(v),du[u][1]++,du[v][0]++;  
    for(int i=1;i<=n;i++) sort(G[i].begin(),G[i].end());
    int S=1,cnt[2]={0,0}; //记录
    bool flag=1; //flag=1表示,所有的节点的入度都等于出度,
    for(int i=1;i<=n;i++)
	{
        if(du[i][1]!=du[i][0])
        {
            flag=0;
            if(du[i][1]-du[i][0]==1/*出度比入度多1*/) cnt[1]++,S=i;
            else if(du[i][0]-du[i][1]==1/*入度比出度多1*/) cnt[0]++;
            else return puts("No"),0;
        }
    }
    if((!flag)&&!(cnt[0]==cnt[1]&&cnt[0]==1)) return !puts("No"),0;
	//不满足欧拉回路的判定条件,也不满足欧拉路径的判定条件,直接输出"No" 
    dfs(S);
    while(!st.empty()) printf("%d ",st.top()),st.pop();
    return 0; 
}
posted @ 2023-09-11 23:48  WW爆米花  阅读(23)  评论(0编辑  收藏  举报