7.19复习笔记

突然忘了二维前缀和怎么写了

dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+map[i][j];
dp[x2][y2]+dp[x1-1][y1-1]-dp[x1-1][y2]-dp[x2][y1-1]

一、高斯消元

图上高斯消元,树上可以用树形dp替代

void Gauss(int rowline,int colline,double a[][maxn]){
	for (int i = 1;i <= rowline;i++){
		int rowId = i;
		for (int j = i+1;j <= rowline;j++){
			if (fabs(a[j][i]) > fabs(a[rowId][i])) rowId = j;
		}
		if (rowId != i){
			for (int j = i;j <= colline+1;j++) swap(a[i][j],a[rowId][j]);
		}
		if (a[i][i] == 0){
			printf("No Solution
");return;
		}
		for (int j = i+1;j <= rowline;j++){
			double tmp = a[j][i]/a[i][i];
			for (int k = i;k <= colline+1;k++){
				a[j][k] -= a[i][k]*tmp;
			}
		}
	}
	for (int i = rowline;i >= 1;i--){
		for (int j = i+1;j <= colline;j++){
			a[i][colline+1] -= a[i][j]*a[j][colline+1];
		}
		a[i][colline+1] /= a[i][i];
	}
	for (int i = 1;i <= rowline;i++) printf("%.2lf
",a[i][colline+1]);
}
int n;double A[maxn][maxn];
int main(){
	n = read();
	for (int i = 1;i <= n;i++){
		for (int j = 1;j <= n+1;j++) scanf ("%lf",&A[i][j]);
	}
	Gauss(n,n,A);
	return 0;
} 

二、整体二分

在信息学竞赛中,有一部分题可以使用二分的办法来解决。但是当这种题目有多次询问且每次询问我们对每个查询都直接二分,可能会收获一个 TLE。这时候我们就会用到整体二分。整体二分的主体思路就是把多个查询一起解决。(所以这是一个离线算法)

可以使用整体二分解决的题目需要满足以下性质:

1.询问的答案具有可二分性

2.修改对判定答案的贡献互相独立,修改之间互不影响效果

3.修改如果对判定答案有贡献,则贡献为一确定的与判定标准无关的值

4.贡献满足交换律,结合律,具有可加性

5.题目允许使用离线算法

算法流程:

记[l,r]为答案的值域,[L,R]为答案的定义域

  • 我们首先把所有操作按照时间的顺序存入数组中,进行分治

  • 在每一层分治中,统计当前查询的答案和mid之间的关系。

  • 根据查询出来的答案和mid间的关系(小于等于mid和大于mid)将当前处理的操作序列分为(q_1)(q_2)两份,并分别递归处理。

  • 当l == r时找到了答案

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#define B cout<<"Breakpoint"<<endl;
#define O(x) cout<<#x<<" "<<x<<endl;
#define o(x) cout<<#x<<" "<<x<<" ";
using namespace std;
int read(){
	int x = 1,a = 0;char ch = getchar();
	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
	return x*a;
}
const int maxn = 505,maxq = 6e4+10,inf = 1e9+7;
int n,Q;
struct node{
	int k,a1,b1,a2,b2,id,op;
}q[maxq+maxn*maxn],q1[maxq+maxn*maxn],q2[maxq+maxn*maxn];
int ans[maxq];
int sum[maxn][maxn];
void add(int x,int y,int k){
	for (int i = x;i <= n;i += i&-i){
		for (int j = y;j <= n;j += j&-j) sum[i][j] += k;
	}
}
int query(int x,int y){
	int res = 0;
	for (int i = x;i;i -= i&-i){
		for (int j = y;j;j -= j&-j) res += sum[i][j];
	}
	return res;
}
void solve(int l,int r,int nl,int nr){
	if (l > r||nl > nr) return;
	if (l == r){
		for (int i = nl;i <= nr;i++) if (q[i].op == 2) ans[q[i].id] = l; 
		return;
	}
	int cnt1 = 0,cnt2 = 0,mid = (l+r >> 1);
	for (int i = nl;i <= nr;i++){
		if (q[i].op == 1){
			if (q[i].k <= mid) add(q[i].a1,q[i].b1,1),q1[++cnt1] = q[i];
			else q2[++cnt2] = q[i];
		}
		else{
			int res = query(q[i].a2,q[i].b2)-query(q[i].a1-1,q[i].b2)-query(q[i].a2,q[i].b1-1)+query(q[i].a1-1,q[i].b1-1);
//			cout<<q[i].a1<<" "<<q[i].b1<<" "<<q[i].a2<<" "<<q[i].b2<<" "<<res<<endl; 
			if (res >= q[i].k) q1[++cnt1] = q[i];
			else q[i].k -= res,q2[++cnt2] = q[i];
		}
	}
//	cout<<cnt1<<" "<<cnt2<<endl;
	for (int i = 1;i <= cnt1;i++) if (q1[i].op == 1) add(q1[i].a1,q1[i].b1,-1);
	for (int i = 1;i <= cnt1;i++) q[nl+i-1] = q1[i];
	for (int i = 1;i <= cnt2;i++) q[nl+cnt1+i-1] = q2[i];
	solve(l,mid,nl,nl+cnt1-1),solve(mid+1,r,nl+cnt1,nr); 
}
int cnt;
int main(){
	n = read(),Q = read();
	for (int i = 1;i <= n;i++){
		for (int j = 1;j <= n;j++){
			int x = read();
			q[++cnt] = (node){x,i,j,0,0,0,1};
		}
	}
	for (int i = 1;i <= Q;i++){
		int a1 = read(),b1 = read(),a2 = read(),b2 = read(),k = read();
		q[++cnt] = (node){k,a1,b1,a2,b2,i,2};
	}
	solve(-inf,inf,1,cnt);
	for (int i = 1;i <= Q;i++) printf("%d
",ans[i]);
	return 0;
}

三、cdq分治

这类问题一般是给你一个长度为 n 的序列,然后让你统计有一些特性的点对(i,j)有多少个,又或者说是找到一对点(i,j)使得一些函数的值最大之类的问题

算法流程:

1.找到当前序列中点mid

2.将所有的点分为三类

第一种是(1leq ileq mid 1leq jleq mid)

第二种是(1leq mid+1 mid+1leq jleq n)

第三种是(mid+1leq ileq n mid+1leq jleq n)

3.将(1,n)序列分为两部分(1,mid)和(mid+1,n)

会发现第一类点和第三类点分别被完全包含在两个序列

这样我们只需要考虑如何求解第二类点

void cdq(int l,int r){
	if (l == r) return;
	int mid = (l+r >> 1);
	cdq(l,mid),cdq(mid+1,r);
	sort(a+l,a+mid+1,cmpy),sort(a+mid+1,a+r+1,cmpy);
	int j = l;
	for (int i = mid+1;i <= r;i++){
		while (a[j].y <= a[i].y&&j <= mid) add(a[j].z,a[j].w),j++;
		a[i].ans += query(a[i].z;)
	}
	for (int i = l;i < j;i++) add(a[i].z,-a[i].w);
}

四、虚树

如果整颗树的节点个数很多,每次查询都遍历一遍复杂度会炸

但是如果保证了每次询问的关键点的Σ在正确的复杂度内,我们可以对关键点建虚树优化

一般我们还会把1作为树根加到虚树中

void build(int x){
	if (top == 0){st[top = 1] = x;return;}
	int LCA = lca(x,st[top]);
	while (top > 1&&dep[LCA] < dep[st[top-1]]) ed2.add(st[top-1],st[top]),top--;
	if (dep[LCA] < dep[st[top]]) ed2.add(LCA,st[top--]);
	if (top == 0||LCA != st[top]) st[++top] = LCA;
	st[++top] = x;
} 
for (int i = 1;i <= q;i++) build(k[i]);
if (top) while (--top) ed2.add(st[top],st[top+1]);
原文地址:https://www.cnblogs.com/little-uu/p/15031783.html