ACM算法模板

读入输出 128 数据

 
inline __int128 read(){
    __int128 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 * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
inline void print(__int128 x){
    if(x < 0){
        putchar('-');
        x = -x;
    }
    if(x > 9)
        print(x / 10);
    putchar(x % 10 + '0');
}
__int128 2^128

线段树 (区间查询都是求和)

线段树单点修改,区间查询

#include<bits/stdc++.h>
#define int long long
#define lc p<<1
#define rc p<<1|1
using namespace std;
const int N=5000005;
int n,t,w[N];
​
struct node
{
    int l,r,sum;
}tr[N*4];
​
void build(int p,int l,int r)
{
    tr[p]={l,r,w[l]};
    if(l==r) return ;
    int m= (l+r)>>1;
    build(lc,l,m);
    build(rc,m+1,r);
    tr[p].sum=tr[lc].sum+tr[rc].sum;
}
​
void update(int p,int x,int k)
{
    if(tr[p].l==x&&tr[p].r==x)
    {
        tr[p].sum+=k;return ;
    }
    int m=(tr[p].l+tr[p].r)>>1;
    if(x<=m) update(lc,x,k);
    if(x>m) update(rc,x,k);
    tr[p].sum=tr[lc].sum+tr[rc].sum;
}
​
int query(int p,int x,int y)
{
    if(x<=tr[p].l&&tr[p].r<=y)
        return tr[p].sum;
    int m=(tr[p].l+tr[p].r)>>1;
    int sum=0;
    if(x<=m) sum+=query(lc,x,y);
    if(y>m) sum+=query(rc,x,y);
    return sum;
}

区间修改 区间查询 (修改操作只有加法)

#include<bits/stdc++.h>
#define int long long
#define lc p<<1
#define rc p<<1|1
using namespace std;
const int N=5000005;
int n,q,w[N];
struct node
{
    int l,r,sum,add;
}tr[N*4];
​
void pushup(int p)
{
    tr[p].sum=tr[lc].sum+tr[rc].sum;
}
void pushdown(int p)
{
    if(tr[p].add)
    {
        tr[lc].sum+=tr[p].add*(tr[lc].r-tr[lc].l+1);
        tr[rc].sum+=tr[p].add*(tr[rc].r-tr[rc].l+1);
        tr[lc].add+=tr[p].add,
        tr[rc].add+=tr[p].add,
        tr[p].add=0;
    }
}
void build(int p,int l,int r)
{
    //cout<<l<<" "<<r<<endl;
    tr[p]={l,r,w[l],0};
    if(l==r) return ;
    int m=(l+r)>>1;
    build(lc,l,m);
    build(rc,m+1,r);
    pushup(p);
}
void update(int p,int x,int y,int k)
{
    if(x<=tr[p].l&&tr[p].r<=y)
    {
        tr[p].sum+=(tr[p].r-tr[p].l+1)*k;
        tr[p].add+=k;
        return ;
    }
    int m=(tr[p].l+tr[p].r)>>1;
    pushdown(p);
    if(x<=m) update(lc,x,y,k);
    if(y>m) update(rc,x,y,k);
    pushup(p);
}
int query(int p,int x,int y)
{
    //cout<<"fFdsf:"<<tr[p].l<<" "<<tr[p].r<<endl;
    if(x<=tr[p].l&&tr[p].r<=y)
        return tr[p].sum;
    int m=(tr[p].l+tr[p].r)>>1;
    //cout<<"m:"<<m<<endl;
    pushdown(p);
    int sum=0;
    //cout<<":"<<x<<" "<<y<<" "<<m<<endl;
    if(x<=m) sum+=query(lc,x,y);
    if(y>m) sum+=query(rc,x,y);
    return sum; 
}

​区间修改 区间查询 (修改操作有乘法有加法)

#include<bits/stdc++.h>
#define int long long
#define lc p<<1
#define rc p<<1|1
using namespace std;
const int N=2e5+5;
const int mod = 1e9 + 7;
int n,q,w[N] ;
char s[N];

struct node
{
	int l,r,sum,mul,add;
}tr[N*4];

void pushup(int p)
{
    tr[p].sum = (tr[lc].sum + tr[rc].sum)%mod ;
}

void pushdown(int p)
{
	if( tr[p].mul != 1)
	{
		tr[lc].sum *= tr[p].mul; tr[lc].sum %= mod;
		tr[rc].sum *= tr[p].mul; tr[rc].sum %= mod;
		
	    tr[lc].mul *= tr[p].mul; tr[lc].mul %= mod;
		tr[rc].mul *= tr[p].mul; tr[rc].mul %= mod;
		
		tr[lc].add *= tr[p].mul; tr[lc].add %= mod;
		tr[rc].add *= tr[p].mul; tr[rc].add %= mod;
		
		tr[p].mul = 1;
	}
	
	if( tr[p].add != 0 )
	{
	    tr[lc].sum += tr[p].add*( tr[lc].r - tr[lc].l + 1 )%mod; tr[lc].sum %= mod;
		tr[rc].sum += tr[p].add*( tr[rc].r - tr[rc].l + 1 )%mod; tr[rc].sum %= mod;
		
		tr[lc].add += tr[p].add; tr[lc].add %= mod;
		tr[rc].add += tr[p].add; tr[rc].add %= mod;
		
		tr[p].add = 0;
	}
	
}
void build(int p,int l,int r)
{
	tr[p]={l,r,w[l],1,0};
	if(l==r) return ;
	int m=(l+r)>>1;
	build(lc,l,m);
	build(rc,m+1,r);
	pushup(p);
}
void update_add(int p,int x,int y,int k)
{
	if(x<=tr[p].l&&tr[p].r<=y)
	{
        tr[p].sum += k*(tr[p].r - tr[p].l + 1)%mod; tr[p].sum %= mod;
		tr[p].add += k; tr[p].add %= mod;
		return ;
	}
	int m=(tr[p].l+tr[p].r)>>1;
	pushdown(p);
	if(x<=m) update_add(lc,x,y,k);
	if(y>m) update_add(rc,x,y,k);
	pushup(p);
}

void update_mul(int p,int x,int y,int k)
{
	if(x<=tr[p].l&&tr[p].r<=y)
	{
        tr[p].sum *= k; tr[p].sum %= mod;
		tr[p].mul *= k; tr[p].mul %= mod;
		tr[p].add *= k; tr[p].add %= mod;
		return ;
	}
	int m=(tr[p].l+tr[p].r)>>1;
	pushdown(p);
	if(x<=m) update_mul(lc,x,y,k);
	if(y>m) update_mul(rc,x,y,k);
	pushup(p);
}

int query(int p,int x,int y)
{		
	if(x<=tr[p].l&&tr[p].r<=y)
		return tr[p].sum;
	int m=(tr[p].l+tr[p].r)>>1;
	//cout<<"m:"<<m<<endl;
	pushdown(p);
	int res=0;
	//cout<<":"<<x<<" "<<y<<" "<<m<<endl;
	if(x<=m) res += query(lc,x,y),res %= mod;
	if(y>m)  res += query(rc,x,y),res %= mod;
	pushup(p);
	return res;
}



void solve()
{
  cin>>n>>q>>mod;
	for(int i=1;i<=n;i++)
		cin>>w[i];
	build(1,1,n);
	
	while(q--)
	{
		int op;
		cin>>op;
		if(op==1) // 乘法
		{
			int x,y,k;
			cin>>x>>y>>k;
			update_mul(1,x,y,k);//f();
		}
		else if(op==2) // 加法 
		{
			int x,y,k;
			cin>>x>>y>>k;
			update_add(1,x,y,k);//f();
		}
		else 
		{
			int x,y;
			cin>>x>>y;
			cout<< query(1,x,y) <<endl;
		}
	}
	
}

signed main()
{
	//	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	
	solve();
	
	
	return 0;
}
/*
  8 10 1000212
  1 2 2 3 4 6 7 8
  1 2 5 3
 */

树链剖分

//lugo 3384
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e5;
int w[N];
vector<int> e[N];
int fa[N] , dep[N] , sz[N] , son[N];
int top[N],id[N],nw[N],tim;  //top[i]:i点的链的最上方    id[i],i点新的编号,nw[i]:i点的值

//区间修改 区间查询 (修改操作只有加法)
#define lc p<<1
#define rc p<<1|1
int n,q,r,mod;
struct node
{
	int l,r,sum,add;
}tr[N*4];

void pushup(int p)
{
	tr[p].sum=tr[lc].sum+tr[rc].sum;  tr[p].sum %= mod;
}
void pushdown(int p)
{
	if(tr[p].add)
	{
		tr[lc].sum+=tr[p].add*(tr[lc].r-tr[lc].l+1); tr[lc].sum %= mod;
		tr[rc].sum+=tr[p].add*(tr[rc].r-tr[rc].l+1); tr[rc].sum %= mod;
		tr[lc].add+=tr[p].add, tr[lc].add %= mod;
		tr[rc].add+=tr[p].add, tr[rc].add %= mod;
		tr[p].add=0;
	}
}
void build(int p,int l,int r)
{
	//cout<<l<<" "<<r<<endl;
	tr[p]={l,r,nw[l],0};
	if(l==r) return ;
	int m=(l+r)>>1;
	build(lc,l,m);
	build(rc,m+1,r);
	pushup(p);
}
void update(int p,int x,int y,int k)
{
	if(x<=tr[p].l&&tr[p].r<=y)
	{
		tr[p].sum+=(tr[p].r-tr[p].l+1)*k%mod; tr[p].sum %= mod;
		tr[p].add+=k; tr[p].add %= mod;
		return ;
	}
	int m=(tr[p].l+tr[p].r)>>1;
	pushdown(p);
	if(x<=m) update(lc,x,y,k);
	if(y>m) update(rc,x,y,k);
	pushup(p);
}
int query(int p,int x,int y)
{
	//cout<<"fFdsf:"<<tr[p].l<<" "<<tr[p].r<<endl;
	if(x<=tr[p].l&&tr[p].r<=y)
		return tr[p].sum%mod;
	int m=(tr[p].l+tr[p].r)>>1;
	//cout<<"m:"<<m<<endl;
	pushdown(p);
	int sum=0;
	//cout<<":"<<x<<" "<<y<<" "<<m<<endl;
	if(x<=m) sum+=query(lc,x,y) , sum %= mod;
	if(y>m) sum+=query(rc,x,y) , sum %= mod;
	return sum; 
}


//更新数组  fa , dep , sz , son
void dfs1(int u,int father)
{
	fa[u] = father, dep[u] = dep[father]+1,sz[u] = 1;
	for(auto v:e[u] )
	{
		if(v==father) continue;
		dfs1(v,u);
		sz[u] += sz[v];
		if( sz[son[u]] < sz[v] ) son[u] = v;
	}
}


/*
把树分成多个链条,让树变成线性,好让树做线段树操作
根到任意点的重链最多有logn条
  
每条链条的开头都是父节点的轻儿子,除了第一个链条(头为根节点)
 */
void dfs2(int u,int t)
{
	top[u] = t;id[u]=++tim;nw[tim]=w[u];
	if(!son[u]) return ;
	dfs2(son[u],t);
	for(auto v:e[u])
	{
		if( v==fa[u] || v==son[u]) continue;
		dfs2(v,v);
	}
}

/*
  通过链条跳到 lca 点
  途径的链条不超过logn条 , 每根链条,它们的索引都是连续的,因为最开始用时间戳来建立的链条
 */
void update_path(int u,int v,int k)
{
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		update(1,id[top[u]],id[u],k);
		u = fa[top[u]];
	}
	if(dep[u]<dep[v]) swap(u,v);
	update(1,id[v],id[u],k);
}

int query_path(int u,int v)
{
	int res = 0;
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		res += query(1,id[top[u]],id[u]); res %= mod;
		u = fa[top[u]];
	}
	
	if(dep[u]<dep[v]) swap(u,v);
	res += query(1,id[v],id[u]); res %= mod;
	return res;
}

void update_xia(int u,int k)
{
	update(1,id[u],id[u]+sz[u]-1,k);
}

int query_xia(int u)
{
	return query(1,id[u],id[u]+sz[u]-1)%mod;
}

signed main()
{
	cin>>n>>q>>r>>mod;
	
	for(int i=1;i<=n;i++)
		cin>>w[i];
	
	for(int i=1;i<n;i++)
	{
		int u,v;
		cin>>u>>v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	
	dfs1(r,0);
	dfs2(r,r);
	
	build(1,1,n);
	
	int x,y,z;
	
	while(q--)
	{
		int op;
		cin>>op;
		if(op==1)
		{
			cin>>x>>y>>z;
			update_path(x,y,z);
		}
		else if(op==2)
		{
			cin>>x>>y;
			cout<<query_path(x,y)<<endl;
		}
		else if(op==3)
		{
			cin>>x>>z;
			update_xia(x,z);
		}
		else 
		{
			cin>>x;
			cout<<query_xia(x)<<endl;
		}
	}
	
	return 0;
}


AC自动机

给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;
struct Tree//字典树 
{
    int fail;//失配指针
    int vis[26];//子节点的位置
    int end;//标记有几个单词以这个节点结尾 
}AC[1000000];//Trie树
int cnt=0;//Trie的指针 
inline void Build(string s)
{
    int l=s.length();
    int now=0;//字典树的当前指针 
    for(int i=0;i<l;++i)//构造Trie树
    {
        if(AC[now].vis[s[i]-'a']==0)//Trie树没有这个子节点
            AC[now].vis[s[i]-'a']=++cnt;//构造出来
        now=AC[now].vis[s[i]-'a'];//向下构造 
    }
    AC[now].end+=1;//标记单词结尾 
}
void Get_fail()//构造fail指针
{
    queue<int> Q;//队列 
    for(int i=0;i<26;++i)//第二层的fail指针提前处理一下
    {
        if(AC[0].vis[i]!=0)
        {
            AC[AC[0].vis[i]].fail=0;//指向根节点
            Q.push(AC[0].vis[i]);//压入队列 
        }
    }
    while(!Q.empty())//BFS求fail指针 
    {
        int u=Q.front();
        Q.pop();
        for(int i=0;i<26;++i)//枚举所有子节点
        {
            if(AC[u].vis[i]!=0)//存在这个子节点
            {
                AC[AC[u].vis[i]].fail=AC[AC[u].fail].vis[i];
                //子节点的fail指针指向当前节点的
                //fail指针所指向的节点的相同子节点 
                Q.push(AC[u].vis[i]);//压入队列 
            }
            else//不存在这个子节点 
                AC[u].vis[i]=AC[AC[u].fail].vis[i];
            //当前节点的这个子节点指向当
            //前节点fail指针的这个子节点 
        }
    }
}
int AC_Query(string s)//AC自动机匹配
{
    int l=s.length();
    int now=0,ans=0;
    for(int i=0;i<l;++i)
    {
        now=AC[now].vis[s[i]-'a'];//向下一层
        for(int t=now;t&&AC[t].end!=-1;t=AC[t].fail)//循环求解
        {
            ans+=AC[t].end;
            AC[t].end=-1;
        } 
    }
    return ans;
}
int main()
{
    int n;
    string s;
    cin>>n;
    for(int i=1;i<=n;++i)
    {
        cin>>s;
        Build(s);
    }
    AC[0].fail=0;//结束标志 
    Get_fail();//求出失配指针
    cin>>s;//文本串 
    cout<<AC_Query(s)<<endl;
    return 0;
}

树上启发式合并

例题,树上数颜色
vector<int>V[MAX];
int co[MAX];
int sz[MAX],hson[MAX];        /// 子树大小和重儿子编号。没有重儿子则编号为0
void dfs1(int x,int f){
    sz[x] = 1;
    for(auto it : V[x]){
        if(it == f)continue;
        dfs1(it,x);
        sz[x] += sz[it];
        if(sz[it] > sz[hson[x]])hson[x] = it;
    }
}
int res = 0;                    /// 目前有多少种不同的颜色
int cnt[MAX],ans[MAX];            /// cnt[i] 表示颜色i的出现次数  ans[i] 表示结点编号i的答案
void go(int x,int f,int dec){
    cnt[co[x]] += dec;                        /// 增  / 删贡献
    if(dec == 1 && cnt[co[x]] == 1)res++;    /// 多了一种颜色
    if(dec ==-1 && cnt[co[x]] == 0)res--;    /// 少了一种颜色
    for(auto it : V[x]){
        if(it == f)continue;
        go(it,x,dec);
    }
}
void dfs2(int x,int f,bool del){
    for(auto it : V[x]){
        if(it == f || it == hson[x])continue;    /// 遍历轻儿子
        dfs2(it,x,true);
    }
    if(hson[x])dfs2(hson[x],x,false);            /// 遍历重儿子
    cnt[co[x]]++;if(cnt[co[x]] == 1)res++;
    for(auto it : V[x]){
        if(it == f || it == hson[x])continue;    /// 增加轻儿子贡献
        go(it,x,1);
    }
    ans[x] = res;
    
​
    if(del)go(x,f,-1);        /// 删除轻儿子贡献
​
}
int main()
{
    int n;scanf("%d",&n);
    for(int i = 1;i < n;++i){
        int ta,tb;scanf("%d%d",&ta,&tb);
        V[ta].push_back(tb);
        V[tb].push_back(ta);
    }
    for(int i = 1;i <= n;++i)scanf("%d",&co[i]);
    dfs1(1,1);
    dfs2(1,1,false);
    int Q;scanf("%d",&Q);
    while(Q--){
        int t;scanf("%d",&t);
        printf("%d\n",ans[t]);
    }
    return 0;
}

主席树

求区间第k小

#include<cstdio>
#include<cstring>
#include<algorithm>
#define mid (l+r)/2
using namespace std;
​
const int N = 200010;
int n, q, m, cnt = 0;
int a[N], b[N], T[N];
int sum[N<<5], L[N<<5], R[N<<5];
​
inline int build(int l, int r)
{
    int rt = ++ cnt;
    sum[rt] = 0;
    if (l < r){
        L[rt] = build(l, mid);
        R[rt] = build(mid+1, r);
    }
    return rt;
}
​
inline int update(int pre, int l, int r, int x)
{
    int rt = ++ cnt;
    L[rt] = L[pre]; R[rt] = R[pre]; sum[rt] = sum[pre]+1;
    if (l < r){
        if (x <= mid) L[rt] = update(L[pre], l, mid, x);
        else R[rt] = update(R[pre], mid+1, r, x);
    }
    return rt;
}
​
inline int query(int u, int v, int l, int r, int k)
{
    if (l >= r) return l;
    int x = sum[L[v]] - sum[L[u]];
    if (x >= k) return query(L[u], L[v], l, mid, k);
    else return query(R[u], R[v], mid+1, r, k-x);
}
​
int main()
{
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; i ++){
        scanf("%d", &a[i]);
        b[i] = a[i];
    }
    sort(b+1, b+1+n);
    m = unique(b+1, b+1+n)-b-1;
    T[0] = build(1, m);
    for (int i = 1; i <= n; i ++){
        int t = lower_bound(b+1, b+1+m, a[i])-b;
        T[i] = update(T[i-1], 1, m, t);
    }
    while (q --){
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        int t = query(T[x-1], T[y], 1, m, z);
        printf("%d\n", b[t]);
    }
    return 0;
}

线性基板子

#include <bits/stdc++.h>  
#define ll long long  
struct PII{ll x,y;};
using namespace std;
const ll N=1e5+5;
ll p[70],d[N],cnt;
ll insert(ll x)
{
    for (int i=62;i>=0;i--)
        if (x>>i)//为什么可以不写x&(1<<i)?,因为我们每次x的最高位都是因为x^=p[i]变为0,这样也能判断最高位
            if (p[i])
                x^=p[i];//如果第i位这个线性基有,同上方的p[6]
    else
    {
        p[i]=x;
        return 1;//1代表插入成功
    }
    return 0; //0代表这个数x能被线性基表示,0需要特判
}
void rebuild()
{
    for (int i=0;i<=62;i++)//从低到高
        for (int j=i-1;j>=0;j--)//看到比i小的,更新
            if (p[i]&(1ll<<j))//如果p[i]的第j位为1,那么就异或成0
                //因为此时如果p[j]=0的话1^0=1不变
                //如果此时p[j]是存有一个向量的话,就能把这一位的数字异或成0,1^1=0,达到减小的目的
                p[i]^=p[j];
    for (int i=0;i<=62;i++)
        if (p[i]) 
            d[++cnt]=p[i];//存入d数组
}
​
//求一个序列的若干个元素异或的最大值
ll maxans()
{
    ll ans=0;
    for(int i=62;i>=0;i--)//记得从线性基的最高位开始
        if((ans^p[i])>ans)ans^=p[i];
    return ans;
} 
​
ll ask_kth(ll x) 
{
    if (x>=(1ll<<cnt)) return -1;//越界,不存在
    ll res=0;
    for (int i=0;i<=62;i++)
        if (x&(1ll<<i)) res^=d[i+1];//二进制分解
    return res;
}
int main(){
    ll n;cin>>n;
    for(int i=1;i<=n;i++)
    {
        ll c;scanf("%lld",&c);
        insert(c);
    }    
    rebuild();
    for(int i=1;i<=cnt;i++)
        cout<<d[i]<<" ";
    cout<<endl;
    return 0;  
}  

缩点

把互相能到的点缩成一个点

#include<bits/stdc++.h>
using namespace std;
​
const int N= 2e5; 
int n,m; 
vector<int> e[N];  
int dfn[N],low[N],tot,a[N];
int instk[N];
int scc[N],siz[N],cnt;
stack<int> q;
​
void tarjan(int x){
    //入x时,盖戳、入栈
    dfn[x]=low[x]=++tot;
      q.push(x) , instk[x]=1;
    for(int y : e[x]){
        if(!dfn[y]){//若y尚未访问
            tarjan(y);
            low[x]=min(low[x],low[y]);//回x时更新low
        }
        else if(instk[y])//若y已访问且在栈中
            low[x]=min(low[x],dfn[y]);//在x时更新low
    }
    //离x时,收集SCC
    if(dfn[x]==low[x]){//若x是SCC的根
        int y; ++cnt;
        do{
            y=q.top();q.pop();//y=stk[top--];
            instk[y]=0;
            scc[y]=cnt;//SCC编号
            ++siz[cnt];//SCC大小
        }while(y!=x);
    }
}
int main(){
​
cin>>n;
for(int i=1;i<=n;i++) 
    cin>>a[i];
for(int i=1; i<=n; i++)//可能不连通
    if(!dfn[i]) tarjan(i);
return 0;
​
}
​
/*
  5 5
  1 2  2 3    3 1    2 5    2 4
 */

马拉车

#include<iostream>
#include<cstring>
using namespace std;
const int N=50000000;
char s[N],ss[N];
int ma[N],id,mx,ans;

int main()
{
    cin>>(ss+1);
    int n=strlen(ss+1);
    for(int i=1;i<=n;i++)
        s[i<<1]=ss[i],s[(i<<1)|1]='#';
    int len=((int)strlen(ss+1)<<1)|1;
    s[0]='&',s[1]='#';
    s[len+1]='.';
    for(int i=1;i<=len;i++)
    {
        //cout<<"id mx->"<<id<<" "<<mx<<endl;
        if(i<mx) ma[i]=min(ma[(id<<1)-i],mx-i);
        while(s[i-ma[i]-1]==s[i+ma[i]+1]) ma[i]++;
        if(i+ma[i]>mx) 
            mx=i+ma[i],id=i;
        ans=max(ans,ma[i]);
    }
    cout<<ans<<endl;
    return 0;
}

求回文串

单源最短路径

#include<bits/stdc++.h>
#define int long long
#define PII pair<int,int> 
using namespace std;    
const int N=5e5+5;
int n,m,p[N],st[N];
vector <pair<int,int> > ed[N]; 

signed main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int a,b,w;
        cin>>a>>b>>w;
        ed[a].push_back({b,w});
    }
    vector<int> d(n+5,2e18);
    d[1]=0;
    priority_queue< PII,vector<PII>,greater<PII> > q;
    q.push({0,1});
    while(q.size())
    {
        int u=q.top().second;q.pop();
        if(st[u]) continue;
        st[u]=1;
        for(auto & [v,dd]:ed[u])
        {
            if(d[v]>d[u]+dd)
            {
                d[v]=d[u]+dd;
                q.push({d[v],v});
            } 
        }
    }
    for(int i=1;i<=n;i++)
        cout<<d[i]<<" \n"[i==n];

    return 0;

}
/*
 4 6 
  1 2 2
  2 3 2
  2 4 1
  1 3 5
  3 4 3
  1 4 4
 */

判断负环

#include<bits/stdc++.h>
#define int long long
#define PII pair<int,int> 
using namespace std;	
const int N=5e5+5;
int n,m;
vector <pair<int,int> > ed[N]; 

int t;
void spfa()
{
	queue<int> q;
	vector<int> d(n+5,2e18),cnt(n+5),st(n+5);
	d[1]=0;
	q.push(1);st[1]=0;
	while(q.size())
	{
		int u=q.front();q.pop();st[u]=0;
		for(int i=0;i<ed[u].size();i++)
		{
			int v=ed[u][i].first,dd=ed[u][i].second;
			if(d[v]>d[u]+dd)
			{
				d[v]=d[u]+dd;cnt[v]=cnt[u]+1;
				if(cnt[v]>=n)
				{
					cout<<"YES\n";return ;
				}
				if(!st[v]) st[v]=1,q.push(v);
			} 
		}
	}
	cout<<"NO\n";return ;
}
signed main()
{
	cin>>t;
	while(t--)
	{
		

		cin>>n>>m;
		for(int i=1;i<=n;i++ ) ed[i].clear();
		for(int i=1;i<=m;i++)
		{
			int a,b,w;scanf("%intd%intd%intd",&a,&b,&w);
			if(w>=0)
				ed[a].push_back({b,w}),ed[b].push_back({a,w});
			else 
				ed[a].push_back({b,w});
		}
		spfa();
	}
	return 0;

}

最近公共祖先

#include <bits/stdc++.h>
#define rep(i, a, b) for (long long i = a; i <= b; ++i)
#define SIZE 400010
#define ll long long
using namespace std;
void io() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); }
int n, m, k, cnt;
int pa[SIZE][21], dep[SIZE];
vector<int> vec[SIZE]; //邻接表

void dfs(int rt, int fin) { //预处理深度和祖先
	pa[rt][0] = fin;
	dep[rt] = dep[pa[rt][0]] + 1; //深度
	for (int i = 1; i < 21; ++i)
		pa[rt][i] = pa[pa[rt][i - 1]][i - 1]; // rt 的 2^i 祖先等价于 rt 的 2^(i-1) 祖先 的 2^(i-1) 祖先
	int sz = vec[rt].size();
	for (int i = 0; i < sz; ++i) {
		if (vec[rt][i] == fin) continue;
		dfs(vec[rt][i], rt);
	}
}

int LCA(int x, int y) {
	if (dep[x] > dep[y]) swap(x, y);
	int tmp = dep[y] - dep[x];
	for (int j = 0; tmp; ++j, tmp >>= 1)
		if (tmp & 1) y = pa[y][j]; //先跳到同一高度
	if (y == x) return x;
	for (int j = 20; j >= 0 && y != x; --j) { //从底往上跳
		if (pa[x][j] != pa[y][j]) { //如果当前祖先不相等 我们就需要更新
			x = pa[x][j];
			y = pa[y][j];
		}
	}
	return pa[x][0];
}

int main() {
	io(); cin >> n >> m;
	rep(i, 1, (n - 1)) {
		int x, y; cin >> x >> y;
		vec[x].emplace_back(y);
		vec[y].emplace_back(x);
	}
	dfs(1, 0);
    
	while(1)
	{
		int a,b;
		cin>>a>>b;
		cout<<LCA(a,b)<<endl;
	}
}
int n,m;
vector<int> ed[N];
int father[N];
int dp[N][20];
int dep[N];

void dfs(int u,int fa)
{
	father[u] = fa;
	dep[u] = dep[fa]+1;
	for(auto v:ed[u] )
	{
		if(v==fa)
			continue;
		dfs(v,u);
	}
}

int lca(int u,int v)
{
	//da   xiao
	if( dep[u]<dep[v] )
		swap(u,v);
	
	for(int i=dep[u]-dep[v] ,j=0 ; i ; i>>=1ll , j++ )
	{
	   	if( i&1ll )
		{
			u = dp[u][j];
		}
	}
	
	if(u==v)
		return u;
	
	for(int i=19;i>=0;i--)
	{
		
		if( dp[u][i] != dp[v][i] )
		{
			u = dp[u][i];
			v = dp[v][i];
		}
	}
	
	return dp[u][0];
}

void init()
{
	dfs(1,0);
	for(int i=1;i<=n;i++)
	  dp[i][0] = father[i];
	for(int i=1;i<20;i++)
	  for(int j=1;j<=n;j++ )
	  {
		  dp[j][i] = dp[dp[j][i-1]][i-1] ;
	  }
}

计算几何

点(x,y)逆时针寻转&度 后得到(x' , y')

数论

欧拉函数

[1-x]中与x 互质的个数

int oula(int x)
{
	int res=x;
	for(int i=2;i*i<=x;i++)
		if(x%i==0)
		{
			res=res/i*(i-1);
			while(x%i==0)
				x/=i;
		}
	if(x>1) 
		res=res/x*(x-1);
	return res;
}

求x的因素的个数

int factor_num(int x)
{
	int res=1;
	for(int i=2;i*i<=x;i++)
		if(x%i==0)
		{
			ll cnum=0;
			while(x%i==0)
				cnum++,x/=i;
			res*=cnum+1;res%=mod;
		}
	if(x>1) 
		res*=2,res%=mod;
	return res;
}

扩展欧几里得

a*x + b*y = gcd( a , b )

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

返回的是a和b的最大公约数

x 的周期是  abs(  b / gcd( a,b ) )

y 的周期是 abs(  a / gcd( a,b )  )

组合学板子

int fpow(int x,int y)
{
	int res = 1;
	while(y)
	{
       if(y&1ll)
		   res *= x , y--, res %= mod;
	   else 
		   x *= x , x %= mod, y>>=1ll;
	}
    return res;
}

const int N=2000005;
int fac[N],inv[N];
void init()
{
    fac[0]=1,inv[0]=1;
    for(int i=1;i<N;i++) 
        fac[i]=fac[i-1]*i%mod;
    inv[N-1]=fpow(fac[N-1],mod-2);
    for(int i=N-2;i>=1;i--) 
        inv[i]=inv[i+1]*(i+1)%mod;
    
}

int C(int aa,int bb)
{
    return fac[bb] %mod* inv[bb-aa]%mod * inv[aa]%mod;
}

整数分块

int block(int st, int ed, int num) {
    //sum(num/i i in [st,ed])
    int L = 0;
    int _ans = 0;
    ed = min(ed, num);
    for (int i = st; i <= ed; i = L + 1) {
        L = min(ed,num / (num / i));  //该区间的最后一个数
        _ans += (L - i + 1)*(num / i);//区间[i,L]的num/i 都是一个值
    }
    return _ans;
} 

该函数的作用是计算指定范围内num/i的和,其中i的取值范围是从sted

小知识点

优先队列

priority_queue <ll,vector<ll>,greater<ll> >q; 栈顶到栈底是从小到大 priority_queue <ll >q;栈顶到栈底是从大到小;

无序哈希

unordered_map 查找效率:非常高,查找的时间复杂度可达到O(1),最坏的情况下卡到O(n) unordered_map 是否有序:无序

排序

sort(a+1,a+n+1,greater<ll>());从大到小 sort(a+1,a+n+1); 从小到大

multiset

自动排序 复杂度(log n)

可以存多个同一种元素

multiset<int> v; v.lower_bound(aim) 在v中找>=aim 的第一个元素 返回指针 v.upper_bound(elem); 返回第一个>aim元素的迭代器。 end(v) 这个位置没有元素,是v中最后一位

v.erase(5) ; 删除所有的5

v.erase( v.find(5) ); 删除一个5

容斥定理

并:(集合的大小)奇正偶负 交:全集减补集的并

小数点的控制

//c++ double a = 0.123 cout<<setprecision(5)<<a<<endl;//输出 0.123

//c printf("%.5lf",a);

输入一行

char s[8];

gets(s); //c

string s;

getline(cin,s);//c++

PI

pi = acos(-1);

数学小知识

map

map<int,int> mp;

mp[5] = 7;

mp.erase(5)  //把5的键值给擦除掉

mp.count(5) //判断是否有5的键值

运算符重载

struct node
{
    int a,d;
    bool operator < (const node &b)const
    {
       return this->d > b.d;
    }
};

用 sort 排序后后从大到小排序

交互题格式

fflush(stdin);清空输入缓冲区

fflush(stdout);清空输出缓冲区

快读

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

其它

1到1e9中有大约有5e7多个质素

#include <bits/stdc++.h>  
#define int long long  
using namespace std;  
int f[340000],g[340000],n;  
void init(){  
	int i,j,m;  
	for(m=1;m*m<=n;++m)f[m]=n/m-1;  
	for(i=1;i<=m;++i)g[i]=i-1;  
	for(i=2;i<=m;++i){  
		if(g[i]==g[i-1])continue;  
		for(j=1;j<=min(m-1,n/i/i);++j){  
			if(i*j<m)f[j]-=f[i*j]-g[i-1];  
			else f[j]-=g[n/i/j]-g[i-1];  
		}  
		for(j=m;j>=i*i;--j)g[j]-=g[j/i]-g[i-1];  
	}  
}  
signed main(){  
	while(scanf("%I64d",&n)!=EOF){  
		init();  
		cout<<f[1]<<endl;  
	}  
	return 0;  
}  

  • 18
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值