目录
核心板子:
关闭同步流
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
\[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
题意:一个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});
}
}//先将边权升序,然后搞并查集
浙公网安备 33010602011771号