核心板子:

关闭同步流

ios::sync_with_stdio(false);

快读

inline int read() {
    #define reg register
    reg int s = 0, t = 0; reg char ch = getchar();
    while(ch > '9' || ch < '0') t |= ch == '-', ch = getchar();
    while(ch >= '0' && ch <= '9') s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return t ? -s : s;
}

gcd/lcm

int gcd(int a, int b) {return b ? gcd(b, a % b) : a; }
int lcm(int a, int b) {return a * b / gcd(a, b);}

欧拉函数

long long oula(long long x) {//欧拉函数
	long long ans = x;
	for (int i = 2; i <= (long long)sqrt(x+0.5); i++) {
		if (x % i == 0) {
			ans = ans / i * (i - 1);
			while (x % i == 0)x /= i;
		}
	}
	if (x > 1)ans = ans / x * (x - 1);
	return ans;
}

STL

priority_queue

priority_queue<int> q;               //通过操作,按照元素从大到小的顺序出队
priority_queue<int,vector<int>, greater<int> > q;   //通过操作,按照元素从小到大的顺序出队

struct cmp {   //自定义优先级
  operator bool ()(int x, int y)   
  {     
     return x > y;   // x小的优先级高    //也可以写成其他方式,如: return p[x] > p[y];表示p[i]小的优先级高
  }
};
priority_queue<int, vector<int>, cmp> q;  //定义方法
//其中,第二个参数为容器类型。第三个参数为比较函数

struct node {   
  int x, y;   
  friend bool operator < (node a, node b)   
  {     
    return a.x > b.x;  //结构体中,x小的优先级高   
  }
};
priority_queue<node>q;  //定义方法
//在该结构中,y为值, x为优先级。
//通过自定义operator<操作符来比较元素中的优先级。
//在重载”<”时,最好不要重载”>”,可能会发生编译错误

unordered_map


multiset

multiset<int>st;
st.count(a);//返回元素值为a的个数
st.find(a);//返回元素值为a的第一个元素,如果没有返回end()
st.lower_bound(a);//返回元素值 >= a的第一个元素位置
st.upper_bound(a);//返回元素值 > a 的第一个元素位置
st.erase(a);//删除与a相等的所有元素,返回被移除的元素个数。
st.erase(pos);//移除迭代器pos所指位置元素,无返回值。

二分

lower_bound upper_bound

vector<int> v;
sort(v.begin(),v.end(),cmp);
int k;//待查找的数字
int p1=lower_bound(v.begin(),v.end(),k)-v.begin();//返回>= a的第一个元素位置
int p2=upper_bound(v.begin(),v.end(),k)-v.begin();//返回> a的第一个元素位置

在单调递增序列a中查找>=x的数中最小的一个(即x或x的后继)

while(l<r)
{
    int mid=(l+r)/2;
    if(a[mid]>=x)
        r=mid;
    else
        l=mid+1;
}
return a[l];

在单调递增序列a中查找<=x的数中最大的一个(即x或x的前驱)

while(l<r)
{
    int mid=(l+r+1)/2;
    if(a[mid]<=x)
        l=mid;
    else
        r=mid-1;
}
return a[l];

哈希

const ll q = 1331, p = (ll)1e9 + 17;//哈希的进制与模
ll po[1001000];//处理区间哈希的进制关系
void ycl() {//预处理
	po[0] = 1;
	for (ll i = 1; i < 1001000; i++) {
		po[i] = (po[i - 1] * q) % p;
	}
}
ll hxz[1001000], hxz2[1001000];//双哈希,更保险
ll hx(ll l, ll r) {//得到区间哈希值
	if (l == 0)return hxz[r];
	return ((hxz[r] - hxz[l - 1] * po[r - l + 1]) % p + p) % p;
}
//hxz[0]=...;
for (int i = 1; i < n; i++) {//得到区间[0,i]的哈希值
		int in; cin >> in;
		hxz[i] = (hxz[i - 1] * q + in) % p;
}

线段树

struct node{
	int l,r;
	long long lazy; 
	long long w;
};
node tree[4*N+100];//要开4倍,不然会炸

void build(int l,int r,int k){//k为当前节点标号
	tree[k].l=l;tree[k].r=r;//左右区间
	tree[k].lazy=0;//懒标记
	if(l==r){//到达叶节点
		cin>>tree[k].w;//读入叶节点点权
		return ;
	}
	int m=(l+r)>>1;
	build(l,m,k<<1);build(m+1,r,k<<1|1);//建子区间
	tree[k].w=tree[k<<1].w+tree[k<<1|1].w;//区间和合并
	return ;
}

void add(int l,int r,int k,long long w){//区间加减法 
//区间 [l,r] 加上 w  当前为k子树
	if(tree[k].l==tree[k].r){//叶节点直接加上w
		tree[k].w+=w;
		return ;	
	}
	if(l<=tree[k].l&&tree[k].r<=r){
        //如果 树节点的区间包含在修改区间内,则举区间修改
		tree[k].w+=(tree[k].r-tree[k].l+1)*w;
		tree[k].lazy+=w;//留下懒标记
		return ;
	}
	push_down(k);//下放懒标记,因为要动 子节点 了 
	int m=(tree[k].r+tree[k].l)>>1;
	if(m>=l){//如果左子树有区间在要修改的区间内
		add(l,r,k<<1,w);
	}
	if(m+1<=r){//右子树
		add(l,r,k<<1|1,w);
	}
	tree[k].w=tree[k<<1].w+tree[k<<1|1].w;//区间合并
} 

void push_down(int k){
	if(!tree[k].lazy)return ;
	tree[k<<1].lazy+=tree[k].lazy;//分别将懒标记下放
	tree[k<<1|1].lazy+=tree[k].lazy;
	tree[k<<1].w+=(tree[k<<1].r-tree[k<<1].l+1)*tree[k].lazy;
	tree[k<<1|1].w+=(tree[k<<1|1].r-tree[k<<1|1].l+1)*tree[k].lazy;//父节点的懒标记给儿子们加上
	tree[k].lazy=0;//父节点的懒标记用完
}

ll getsum(int l,int r,int k){//查询区间和 
	if(l<=tree[k].l&&tree[k].r<=r){
		return tree[k].w;//如果节点区间在待查区间内,直接返回
	}
	push_down(k); //要对儿子们访问了,懒虫动起来
	int m=(tree[k].l+tree[k].r)>>1;
	ll ans=0;
	if(l<=m)ans+=getsum(l,r,k<<1);
	if(m+1<=r)ans+=getsum(l,r,k<<1|1);
	return ans;
}

树状数组

int lowbit(int x) {//获取x二进制最后一位1的位置并转回十进制
	return x & -x;
}
void add(int p) {//单点添加1
	while (p < N) {
		b[p]++;
		p += lowbit(p);
	}
}
int ask(int p) {//询问[1,p]区间和
	int ans = 0;
	while (p) {
		ans += b[p];
		p -= lowbit(p);
	}
	return ans;
}

字典树

//结构体定义:
struct Trie{
	Trie* next[26];
	int num;
	Trie(){  //构造函数,初始化 
		for(int i=0;i<26;i++) next[i]=NULL;
		num = 0;
	}
};
Trie root; //定义根节点
void insert(char str[]){
	Trie* p=&root;
	for(int i=0; str[i] ;i++){
		//节点不存在,就新添加
		if(p->next[str[i]-'a']==NULL) p->next[str[i]-'a'] = new Trie;
		p = p->next[str[i]-'a'];
		p->num++;
	}
} 
int find(char str[]){
	Trie* p=&root;
	for(int i=0; str[i] ;i++){
		if(p->next[str[i]-'a']==NULL) return 0;
		p = p->next[str[i]-'a'];
	}
	return p->num;
}
int main(){
	char str[11];
	while(gets(str)){
		if(!strlen(str)) break;
		insert(str);
	}
	while(gets(str)) cout<<find(str)<<endl;
	return 0;
}

单调栈

stack<int>st;
    int n;cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i]; 
    a[n+1]=1e9;//用于丢出最后元素
    for(int i=1;i<=n+1;i++){
        while(st.size()&&a[st.top()]<=a[i]){
            ans[st.top()]=i-st.top()-1;
            //cout<<"pop"<<st.top()<<endl;
            st.pop();
        }
        st.push(i);
    }

Dijkstra算法(迪杰斯特拉)

//假设从点 k开始
int vis[N];//是否访问过
memset(vis,0,sizeof(vis));
for(int i=0;i<n;i++)d[i]=INF;//初始化为无穷大
d[k]=0;//k到k是0
for(int i=0;i<n;i++){
	int x,m=INF;i
    for(int y=0;y<n;y++){
        if(!vis[y]&&d[y]<=m){
            x=y;//在所有未标号的节点中选出d最小的节点x
            m=d[y];
        }
    }
    vis[x]=1;//标记x用过了
    for(int y=0;y<n;y++){//所有点对x点的最短路更新
        d[y]=min(d[y],d[x]+w[x][y]);
    }
}

并查集

void init() {//初始化
    for (int i = 1; i <= n; ++i) {
        father[i] = i;
    }
}
int get(int x) {//查找
    if (father[x] == x) { // x 结点就是根结点
        return x; 
    }
    return get(father[x]); // 返回父结点的根结点
}
void merge(int x, int y) {//合并
    x = get(x);
    y = get(y);
    if (x != y) { // 不在同一个集合
        father[y] = x;
    }
}
int get(int x) {//优化的get
    if (father[x] == x) { // x 结点就是根结点
        return x; 
    }
    return father[x] = get(father[x]); // 返回父结点的根结点,并另当前结点父结点直接为根结点
}

快速幂

int QuickPow(int x,int N){//x为底,N为幂
    int res = x;
    int ans = 1;
    while(N){
        if(N&1){
            ans *=res;
        }//如果N为奇数,则用掉奇数,使之为偶数
        res*=res;//如果N为偶数,则将res的N次方变为res^2的N/2次方
        N>>=1;
	}
    return ans;
}

矩阵快速幂

前置条件是方阵。

const int maxn = 1e3;
struct Matrix {//结构体代表矩阵类型
    int m[maxn][maxn] = { {0} };
}ans, res;
Matrix Mul(Matrix A, Matrix B, int n) {//矩阵乘法
    Matrix tmp;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            for (int k = 1; k <= n; k++) {
                tmp.m[i][j] += A.m[i][k] * B.m[k][j];
            }
        }
    }
    return tmp;
}

//矩阵快速幂
void QuickPower(int N, int n) {//N为幂,n为大小
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            ans.m[i][i] = (int)(i == j);        
    while (N) {
        if (N & 1)ans = Mul(ans, res, n);
        res = Mul(res, res, n);
        N >>= 1;
    }
}

例题

C - Recursive sequence

HDU - 5950

\[a_n=a_{n-1}+2a_{n-2}+n^4\\a_1=a,a_2=b \]

//#include<bits/stdc++.h>
#include<iostream>
using namespace std;
#define ll  long long
#define int ll
#define endl '\n'
#define wtnnb ios::sync_with_stdio(false)
//const int N = 1e6 + 10;
const int maxn = 8; int mo = 2147493647;
struct Matrix {//结构体代表矩阵类型
    int m[maxn][maxn] = { {0} };
}ans, res;
Matrix Mul(Matrix A, Matrix B, int n) {//矩阵乘法
    Matrix tmp;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            tmp.m[i][j] = 0;
            for (int k = 1; k <= n; k++) {
                tmp.m[i][j] += (A.m[i][k] * B.m[k][j]) % mo;
                tmp.m[i][j] %= mo;
            }
        }
    }
    return tmp;
}

//矩阵快速幂
void QuickPower(int N, int n) {//N为幂,n为大小
    int aa[8][8] = {
        {0,0,0,0,0,0,0,0},
        {0,1,2,1,0,0,0,0},
        {0,1,0,0,0,0,0,0},
        {0,0,0,1,1,1,1,1},
        {0,0,0,0,1,2,3,4},
        {0,0,0,0,0,1,3,6},
        {0,0,0,0,0,0,1,4},
        {0,0,0,0,0,0,0,1}
    };
    memcpy(res.m, aa, sizeof(aa));

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            ans.m[i][j] = (int)(i == j);
    while (N) {
        if (N & 1)ans = Mul(ans, res, n);
        res = Mul(res, res, n);
        N >>= 1;
    }
}
void solve() {
    int n, a, b;
    cin >> n >> a >> b;
    if (n == 1)cout << a << endl;
    else if (n == 2)cout << b << endl;
    else {
        QuickPower(n - 2, 7);
        int pp[8] = { 0,b,a,3 * 3 * 3 * 3,4 * 3 * 3 * 3,6 * 3 * 3,4 * 3,1 };
        ll anss = 0;
        for (int i = 1; i <= 7; i++)anss = (anss + pp[i] * ans.m[1][i]) % mo;
        cout << anss % mo << endl;
    }

}
signed main() {
    wtnnb;
    int t = 1;
    cin >> t;
    while (t--)solve();
}

重点在找出关系矩阵

如:

HDU - 5950

\[a_n=a_{n-1}+2a_{n-2}+n^4\\a_1=a,a_2=ba_n=a_{n-1}+2a_{n-2}+n^4\\a_1=a,a_2=b \]

\[a_n\\a_{n-1}\\(n+1)^4\\4(n+1)^3\\6(n+1)^2\\4(n+1)\\1 \]

\[a_{n-1}\\a_{n-2}\\n^4\\4n^3\\6n^2\\4n\\1 \]

根据上式可以得到关系矩阵:

		{0,0,0,0,0,0,0,0},
        {0,1,2,1,0,0,0,0},
        {0,1,0,0,0,0,0,0},
        {0,0,0,1,1,1,1,1},
        {0,0,0,0,1,2,3,4},
        {0,0,0,0,0,1,3,6},
        {0,0,0,0,0,0,1,4},
        {0,0,0,0,0,0,0,1}

初始矩阵:

int pp[8] = { 0,b,a,3 * 3 * 3 * 3,4 * 3 * 3 * 3,6 * 3 * 3,4 * 3,1 };

高斯消元法

浮点类型解

int Gauss ( int equ, int var ) {
	for ( int i = 0;i <= var;i ++ ) {
		ans[i] = 0;
		Free[i] = 1;
	}
	int row, col, MaxRow;
	col = 0;
	for ( row = 0;row < equ && col < var;row ++, col ++ ) {
		MaxRow = row;
		for ( int i = row + 1;i < equ;i ++ ) 
			if ( fabs ( a[i][col] ) > fabs ( a[MaxRow][col] ) )
				MaxRow = i;
		if ( MaxRow != row ) {
			for ( int i = row;i <= var;i ++ )
				swap ( a[row][i], a[MaxRow][i] );
		}
		if ( fabs ( a[row][col] ) < eps ) {
			row --;
			continue;
		}
		for ( int i = row + 1;i < equ;i ++ ) {
			if ( fabs ( a[i][col] ) > eps ) {
				double temp = a[i][col] / a[row][col];
				for ( int j = col;j <= var;j ++ )
					a[i][j] -= a[row][j] * temp;
				a[i][col] = 0;
			}
		}
	}
	for ( int i = row;i < equ;i ++ )
		if ( fabs ( a[i][col] ) > eps )
			return -1;
	double temp;
	if ( row < var ) {
		for ( int i = row - 1;i >= 0;i -- ) {
			int free_num = 0, idx;
			for ( int j = 0;j < var;j ++ )	
				if ( a[i][j] && Free[j] ) {
					free_num ++;
					idx = j;
				}
			if ( free_num > 1 )
				continue;
			temp = a[i][var];
			for ( int j = 0;j < var;j ++ ) {
				if ( a[i][j] && j != idx )
					temp -= a[i][j] * ans[j];
			}
			ans[idx] = temp / a[i][idx];
			Free[idx] = 0;
		}
		return var - row;
	}
	for ( int i = var - 1;i >= 0;i -- ) {
		temp = a[i][var];
		for ( int j = i + 1;j < var;j ++ )
			if ( a[i][j] )
				temp -= a[i][j] * ans[j];
		ans[i] = temp / a[i][i];
	}
	return 0;
}



整数类型解

int a[maxn][maxn];
int ans[maxn];

int GCD ( int a, int b ) {
	if ( ! b )
		return a;
	return GCD ( b, a % b );
}
int LCM ( int a, int b ) {
	return a / GCD ( a, b ) * b;
}
int Fabs ( int x ) {
	if ( x < 0 )
		return -x;
	return x;
}

int Gauss ( int equ, int var ) {
	for ( int i = 0;i <= var;i ++ ) {
		ans[i] = 0;
		Free[i] = 1;
	}
	int row, col, MaxRow;
	col = 1;
	for ( row = 1;row <= equ && col < var;row ++, col ++ ) {
		MaxRow = row;
		for ( int i = row + 1;i <= equ;i ++ )
			if ( Fabs ( a[i][col] ) > Fabs ( a[MaxRow][col] ) )
				MaxRow = i;
		if ( MaxRow != row ) {
			for ( int i = row;i <= var;i ++ )
				swap ( a[row][i], a[MaxRow][i] );
		}
		if ( ! a[row][col] ) {
			row --;
			continue;
		}
		for ( int i = row + 1;i <= equ;i ++ ) {
			if ( a[i][col] ) {
				int lcm = LCM ( Fabs ( a[i][col] ), Fabs ( a[row][col] ) );
				int T1 = lcm / Fabs ( a[i][col] );
				int T2 = lcm / Fabs ( a[row][col] );
				if ( a[i][col] * a[row][col] < 0 )
					T2 = -T2;
				for ( int j = col;j <= var;j ++ )
					a[i][j] = a[i][j] * T1 - a[row][j] * T2;
			}
		}
	}
	for ( int i = row;i <= equ;i ++ )
		if ( a[i][col] )
			return -1;
	int temp;
	if ( row < var ) {
		return var - row;
	}
	for ( int i = var - 1;i > 0;i -- ) {
		temp = a[i][var];
		for ( int j = i + 1;j < var;j ++ )
			if ( a[i][j] )
				temp -= a[i][j] * ans[j];
		ans[i] = temp / a[i][i];
	}
	return 0;
}


模线性方程组

int Gauss(int equ,int var){
	int row,col=0;
	for(row=0;row<equ&&col<var;row++,col++){
		int MaxRow=row;
		for(int i=row+1;i<equ;i++)
			if(abs(a[i][col])>abs(a[MaxRow][col]))
				MaxRow=i;
		if(row!=MaxRow){
			for(int i=row;i<=var;i++)
				swap(a[row][i],a[MaxRow][i]);
		}
		if(!a[row][col]){row--;continue;}
		for(int i=row+1;i<=equ;i++){
			if(a[i][col]) {
                int T=a[i][col]*q_pow(a[row][col],mod-2,mod)%mod;
				for(int j=col;j<=var;j++){
					a[i][j]=(a[i][j]-a[row][j]*T%mod+mod)%mod;
				}
			}
		}
	}
	for(int i=row;i<=equ;i++)
		if(a[i][col])return -1;
	if(row<var)return var-row;
	for(int i=var-1;i>=0;i--){
		int temp=a[i][var];
		for(int j=i+1;j<var;j++){
			if(a[i][j]){
				temp-=a[i][j]*x[j];
				temp=(temp%mod+mod)%mod;
			}
		}
		x[i]=temp*q_pow(a[i][i],mod-2,mod)%mod;
	}
	return 0;
}


异或类型解

bitset<222>a[222];
int ans[222],Free[222],cnt;
int Gauss(int equ,int var){
	int row,col,MaxRow;
	col=0;
	for(row=0;row<equ&&col<var;row++,col++){
		MaxRow=row;
		for(int i=row+1;i<equ;i++) 
			if(abs(a[i][col])>abs(a[MaxRow][col]))
				MaxRow=i;
		if(MaxRow!=row){
			swap(a[row],a[MaxRow]);
		}
		if(a[row][col]==0){
			row--;
			Free[++cnt]=col;
			continue;
		}
		for(int i=row+1;i<equ;i++){
			if(a[i][col]){
				a[i]^=a[row];
			}
		}
	}
	for(int i=row;i<equ;i++ )
		if(a[i][col])
			return -1;
	if(row<var) 
		return var-row;
	for(int i=var-1;i>=0;i--){
		ans[i]=a[i][var];
		for(int j=i+1;j<var;j++)
			if(a[i][j])
				ans[i]^=(a[i][j]&&ans[j]);
	}
	return 0;
}


例题

D - EXTENDED LIGHTS OUT

POJ - 1222

题意:一个5*6的01矩阵,任意次操作:选择一个点,将它和它周围紧挨的一位翻转。问最后全1,要选择哪些点操作。

列方程组,每个点的状态与紧挨的点有关,其它点无关。

高斯消元法

异或类型解

对每个点列长度31(30+1的增广矩阵)的方程,共30个方程

//#include<bits/stdc++.h>
#include<algorithm>    
#include<iostream>   
#include<bitset>

using namespace std;
#define ll  long long
//#define int ll
#define endl '\n'
#define wtnnb ios::sync_with_stdio(false)
const int N = 32;
const int maxn = 32;

bitset<222>a[222];
int ans[222], Free[222], cnt;

int Gauss(int equ, int var) {
	int row, col, MaxRow;
	col = 0;
	for (row = 0; row < equ && col < var; row++, col++) {
		MaxRow = row;
		for (int i = row + 1; i < equ; i++)
			if (abs(a[i][col]) > abs(a[MaxRow][col]))
				MaxRow = i;
		if (MaxRow != row) {
			swap(a[row], a[MaxRow]);
		}
		if (a[row][col] == 0) {
			row--;
			Free[++cnt] = col;
			continue;
		}
		for (int i = row + 1; i < equ; i++) {
			if (a[i][col]) {
				a[i] ^= a[row];
			}
		}
	}
	for (int i = row; i < equ; i++)
		if (a[i][col])
			return -1;
	if (row < var)
		return var - row;
	for (int i = var - 1; i >= 0; i--) {
		ans[i] = a[i][var];
		for (int j = i + 1; j < var; j++)
			if (a[i][j])
				ans[i] ^= (a[i][j] && ans[j]);
	}
	return 0;
}


void clears() {
	for (int i = 0; i < maxn;i++)
		for (int j = 0; j < maxn; j++) {
			a[i][j] = 0;
			ans[i] = 0;
			Free[i] = 0;
		}
}

int PUZZLE = 0;
void solve() {
	clears();
    int jz[5][6];
    for (int i = 0; i < 5; i++)
        for (int j = 0; j < 6; j++)
            cin >> jz[i][j];
	cout << "PUZZLE #" << ++PUZZLE << endl;
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 6; j++) {
            int k = i * 6 + j;
            a[k][30] = jz[i][j];
            for (int ii = 0; ii < 5; ii++) {
                for (int jj = 0; jj < 6; jj++) {
                    int kk = ii * 6 + jj;
					if (abs(ii - i) + abs(jj - j) <= 1)a[k][kk] = 1;
                }
            }
        }
    }
	int freeNum = Gauss(30, 30);
	for (int i = 0; i < 5; i++)
		for (int j = 0; j < 6; j++)
			cout << ans[i * 6 + j] << " \n"[j == 6 - 1];
}
signed main() {
    wtnnb;
    int t = 1;
    cin >> t;
    while (t--)solve();
}


dp

背包

01背包

#include <iostream>//这个是对的
#include <algorithm>
using namespace std;
#define Mmax 12890
#define Nmax 3410
struct sl{
    int w,d;//魅力d,权重w;
}a[Nmax];
int dp[Nmax][Mmax];
int main(void){
    int n,m;cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i].w>>a[i].d;
    for(int i=1;i<=n;i++){
        for(int w=0;w<=m;w++){
            if(a[i].w>w)dp[i][w]=dp[i-1][w];
            else dp[i][w]=max(dp[i-1][w],dp[i-1][w-a[i].w]+a[i].d);
            //cout<<"i: "<<i<<",w: "<<w<<"dp: "<<dp[i][w]<<endl;
        }
    }
    cout<<dp[n][m]<<endl;  
    return 0;
}

完全背包

#include<cstdio>
#include<algorithm>
using namespace std;
int w[300],c[300],f[300010];
int V,n;
int main()
{
    scanf("%d%d",&V,&n);
    for(int i=1; i<=n; i++)
    {
        scanf("%d%d",&w[i],&c[i]);
    }
    for(int i=1; i<=n; i++)
        for(int j=w[i]; j<=V; j++)//注意此处,与0-1背包不同,这里为顺序,0-1背包为逆序
            f[j]=max(f[j],f[j-w[i]]+c[i]);
    printf("max=%d\n",f[V]);
    return 0;
}

状压dp


树形dp


逆元

long long q_pow(long long a,long long b,long long mo){
    long long res = 1;
    while(b){
        if(b&1)res*=a;
        a*=a;
        b>>=1;
        a%=mo;
        res%=mo;
    }
    return res;
}
long long inv(long long a,long long mo){
    return q_pow(a,mo-2,mo);
}

倍增

在单调数组中查值k

int l = 0;//查找下标
int p = 1;//查找范围
while(p){//还能向后找的话
    if(l+p<n && a[l+p]<k){//如果被查值比当前范围还大
        l+=p;//查找出发点更新
        p<<=1;//扩大查找半径
    }
    else {//被查数在范围里
        p>>=1;//缩小查找半径
    }
}
cout<<a[l];//此时的l即为查找目标位置

st

求解 RMQ 问题

著名的 ST 算法就是倍增的产物,ST 算法主要用于解决多次询问区间最值 RMQ 的问题。

递推式

\[f_{i,j}=max(f_{i,j-1},f_{i+2^{j-1},j-1}) \]

f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);

【问题2】给出 n 个数,有 m 次询问,每次输出区间 [x,y] 的最大值。

结合 DP,设 f[i][j] 表示从 i 开始的长度为 2j 的区间的最大值,那么容易得出方程:f[i][j]=max(f[i][j-1],f[i+2(j-1)][j-1])。其中 f[i][j-1] 表示区间 [i,i+2(j-1)-1] 的最大值,f[i+2(j-1)][j-1] 表示区间 [i+2(j-1),i+2j]的最大值。

现在要处理询问了。从 x,y 中我们可以找到一个值 k,使得 [x,x+2k-1] 和 [y-2k+1,y]可以正好覆盖 [x,y] 整个区间,那么 [x,y] 的区间最大值就是 max(f[x][k],f[y-2k,k])。cmath库的函数 log2 可以帮我们c++解决求 k 的问题。



ST算法预处理(求出 f 数组)

for(int i=1;i<=n;i++)f[i][0]=a[i];//一个数的区间就是其本身
for(int j=1;(1<<j)<=n;j++)
    for(int i=1;i+(1<<j)-1<=n;i++)
        f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);


ST算法询问过程

    cin>>x>>y;
    int k=log2(y-x+1);
    cout<<max(f[x][k],f[y-(1<<k)+1][k]<<endl;

int query(int l,int r){
    int x=log2(r-l+1);
    return std::max(st[l][x],st[r-(1<<x)+1][x]);
}

总算法复杂度为 O(nlogn+m)(预处理O(nlogn);询问每次O(1),m次O(m))

LCA

普通LCA倍增法在线

const int N = 5e4 + 9;
vector<int>to[N],ot[N];
bool vis[N];
int dep[N];//深度

int fa[N][30];
void clear(int n) {
    for (int i = 0; i <= n; i++) {
        to[i].clear(); ot[i].clear();
        vis[i] = 0;
        dep[i] = 0;
        for(int j=0;j<30;j++)fa[i][j] = 0;
    }

}
void dfs(int son, int far) {
    fa[son][0] = far;
    dep[son] = dep[far] + 1;
    //depw[son]=depw[son]+son.w 如果带权的话,再定义一个数组
    for (int k = 0; k < to[son].size(); k++) {
        int i = to[son][k];
        if (i != far) {
            //vis[i] = 1;
            dfs(i, son);
        }
    }
}
int lca(int x, int y) {
    if (x == y)return x;
    if (dep[y] > dep[x])swap(x, y);
    for (int i = 20; i >= 0; i--) {
        if (fa[x][i] && dep[fa[x][i]] >= dep[y])x = fa[x][i];
    }
    if (x == y)return x;
    for (int i = 20; i >= 0; i--) {
        if (fa[x][i] != 0 && fa[y][i] != 0 && fa[x][i] != fa[y][i]) {
            x = fa[x][i];
            y = fa[y][i];
        }
    }
    return fa[x][0];
}
void init(int n) {//先调用dfs预处理出fa[][0],再调用init
    for (int i = 1; i <= 20; i++) {
        for (int j = 1; j <= n; j++) {
            fa[j][i] = fa[fa[j][i - 1]][i - 1];
        }
    }
}

带权LCA倍增法在线

//#include<bits/stdc++.> 
#include<iostream> 
#include<algorithm> 
#include<vector>
using namespace std;
#define ll  long long
//#define int ll
#define endl '\n'
#define wtnnb ios::sync_with_stdio(false)
const int N = 5e4 + 9;
vector<pair<int, int>>to[N];
int dep[N];//深度
int depw[N];//带权深度!!!
int fa[N][21];
void clear(int n) {
    for (int i = 0; i <= n; i++) {
        to[i].clear();
        dep[i] = 0;
        depw[i] = 0;
        fa[i][0] = 0;
    }
}
void dfs(pair<int, int> son, int far) {
    fa[son.first][0] = far;
    dep[son.first] = dep[far] + 1;
    depw[son.first] = depw[far] + son.second;
    for (int k = 0; k < to[son.first].size(); k++) {
        pair<int, int> i = to[son.first][k];
        if (i.first != far) {
            dfs(i, son.first);
        }
    }
}
int lca(int x, int y) {
    if (x == y)return x;
    if (dep[y] > dep[x])swap(x, y);
    for (int i = 20; i >= 0; i--) {
        if (fa[x][i] && dep[fa[x][i]] >= dep[y])x = fa[x][i];
    }
    if (x == y)return x;
    for (int i = 20; i >= 0; i--) {
        if (fa[x][i] != 0 && fa[y][i] != 0 && fa[x][i] != fa[y][i]) {
            x = fa[x][i];
            y = fa[y][i];
        }
    }
    return fa[x][0];
}
void init(int n) {
    for (int i = 1; i <= 20; i++) {
        for (int j = 1; j <= n; j++) {
            fa[j][i] = fa[fa[j][i - 1]][i - 1];
        }
    }
}
void solve() {
    int n, m; cin >> n >> m;
    clear(n);
    for (int i = 1; i < n; i++) {
        int a, b, k; cin >> a >> b >> k;
        to[a].push_back({ b,k });
        to[b].push_back({ a,k });
    }
    dfs({ 1,0 }, 0);
    init(n);
    while (m--) {
        int a, b; cin >> a >> b;
        int c = lca(a, b);
        int ans = abs(depw[c] - depw[a]) + abs(depw[c] - depw[b]);
        cout << ans << endl;
    }

}
signed main() {
    wtnnb;
    int t = 1;
    cin >> t;
    while (t--)solve();
}


离线

Tarjan 算法

const int N = 2e4 + 9;
vector<int>to[N];
int dfn[N], low[N], co[N]; int cnt = 0; stack<int>st; int vis[N]; int color = 0;
int d[N];//缩点后出度
void tarjan(int now) {
    dfn[now] = low[now] = ++cnt;//初始化
    st.push(now);//入栈
    vis[now] = 1;//标记已入栈
    for (auto i : to[now]) {//遍历可达点
        if (!dfn[i]) {//如果没有被搜索过
            tarjan(i);
            low[now] = min(low[now], low[i]);//更新low
        }
        else if (vis[i]) {//如果被搜索过
            low[now] = min(low[now], dfn[i]);
        }
    }
    if (dfn[now] == low[now]) {
        color++;
        co[now] = color;//缩点标记颜色
        while (st.top() != now) {//出栈
            co[st.top()] = color;
            vis[st.top()] = 0;
            st.pop();
        }
        vis[now] = 0; st.pop();
    }   
}
void buildNew(int n) {//建缩点后的新有向无环图
    for (int i = 1; i <= n; i++) {
        for (auto j : to[i]) {
            if (co[i] != co[j]) {
                toNewSet[co[i]].insert(co[j]);
            }
        }
    }
    for (int i = 1; i < cnt; i++) {
        for (auto j : toNewSet[i]) {
            toNew[i].push_back(j);
            toNew[j].push_back(i);
        }
    }
}

最小生成树

void Kruskal(){//最小生成树
    for(Edge i:edge){
        int x=find(i.from);
        int y=find(i.to);
        if(x==y)continue;
        fa[x]=y;
        to[x].push_back({y,i.k});to[y].push_back({x,i.k});
    }
}//先将边权升序,然后搞并查集

posted on 2022-07-14 16:40  wtn135687  阅读(125)  评论(0)    收藏  举报