一.前缀和(一维,二维,三维)
模板
/*
一维前缀和
s[i] = s[i-1] + a[i];
查询 l - r 的和
s[r] - s[l-1]
*/
/*
二维前缀和
s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j];
查询 (x1,y1) - (x2,y2) 的和
s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]
*/
/*
三维前缀和
s[i][j][k] = s[i-1][j][k] + s[i][j-1][k] + s[i][j][k-1]
- s[i-1][j-1][k] - s[i-1][j][k-1] - s[i][j-1][k-1]
+ s[i-1][j-1][k-1] + a[i][j][k];
查询(x1,y1,z1) - (x2,y2,z2) 的和
s[x2][y2][z2] - s[x1-1][y1-1][z1-1] - s[x1-1][y2][z2] - s[x2][y1-1][z2] - s[x2][y2][z1-1]
+ s[x1-1][y1-1][z2] + s[x1-1][y2][z1-1] + s[x2][y1-1][z1-1];
*/
例题
Kobolds and Catacombs

思路:能够分开的点 前面的所有数一定小于后面所有数 可以利用前缀和来判断 s表示原数组的前缀和 presum 表示 排成升序的前缀和 当s[i] == presum[i] 时 ans++
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1000010;
LL a[N];
LL presum[N];
LL s[N];
int ans;
int main()
{
int n;
cin >> n;
for(int i = 1;i <= n;i++)
{
cin >> a[i];
s[i] += s[i-1] + (LL)a[i];
}
sort(a + 1,a + n + 1);
for(int i = 1;i <= n;i++) presum[i] += presum[i-1] + (LL)a[i];
bool flag = true;
for(int i = 1;i <= n;i++)
{
if(s[i] == presum[i]) ans++;
else flag = false;
}
cout << ans << endl;
}
Cuboid Sum Query(三维前缀和 模板)

#include<bits/stdc++.h>
using namespace std;
const int N = 110;
typedef long long LL;
int a[N][N][N];
LL s[N][N][N];
int main()
{
int n;
cin >> n;
for(int i = 1;i <= n;i++)
for(int j = 1;j <= n;j++)
for(int k = 1;k <= n;k++)
cin >> a[i][j][k];
for(int i = 1;i <= n;i++)
for(int j = 1;j <= n;j++)
for(int k = 1;k <= n;k++)
s[i][j][k] = s[i-1][j][k] + s[i][j-1][k] + s[i][j][k-1]
- s[i-1][j-1][k] - s[i-1][j][k-1] - s[i][j-1][k-1]
+ s[i-1][j-1][k-1] + (LL)a[i][j][k];
int Q;
cin >> Q;
while(Q--)
{
int x1,x2,y1,y2,z1,z2;
cin >> x1 >> x2 >> y1 >> y2 >> z1 >> z2;
LL sum = s[x2][y2][z2] - s[x1-1][y1-1][z1-1]
- s[x1-1][y2][z2] - s[x2][y1-1][z2] - s[x2][y2][z1-1]
+ s[x1-1][y1-1][z2] + s[x1-1][y2][z1-1] + s[x2][y1-1][z1-1] ;
cout << sum << endl;
}
}
二.二分(二分查找,二分答案)
模板
/*
二分模板一
int l = 1,r = le9;
while(l <= r)
{
int mid = l + r >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
return r;
*/
/*
二分模板二
int l = 1,r = le9;
while(l <= r)
{
int mid = l + r + 1 >> 1;
if(check(mid)) l = mid;
else r = mid - 1;
}
return l;
*/
例题
MaratonIME gets candies(交互题 猜数字)
#include<bits/stdc++.h>
using namespace std;
char ask(int x)
{
cout << "Q" << " " << x << endl;
char res;
cin >> res;
return res;
}
int main()
{
int l = 1,r = 1e9;
while(l <= r)
{
int mid = l + r >> 1;
char op = ask(mid);
if(op == '=') break;
else if( op == '<') r = mid - 1;
else l = mid + 1;
}
}
Counting Pairs(二分边界范围)
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL N = 200010;
LL a[N];
LL n;
LL findl(LL x)//找到大于等于x的最左边的数的下标
{
LL l = 0,r = n-1;
while(l < r)
{
LL mid = l + r >> 1;
if(a[mid] >= x) r = mid;
else l = mid + 1;
}
return r;
}
LL findr(LL x)//找到小于等于x的最右边的数的下标
{
LL l = 0,r = n - 1;
while(l < r)
{
LL mid = l + r + 1>> 1;
if(a[mid] <= x) l = mid;
else r = mid - 1;
}
return l;
}
void solve()
{
memset(a,0,sizeof a);//每次初始化一下a数组
LL x,y;
cin >> n >> x >> y;
LL sum = 0;
for(LL i = 0;i < n;i++)
{
cin >> a[i];
sum += a[i];//求一下总和
}
/*
要求的是 x <= sum - a[i] - a[j] <= y
那么我们可以枚举每一个a[i] 那么对应的a[j] 就有范围
sum - a[i] - y <= a[j] <= sum - a[i] - x
因为我们对元素组排序 所以可以二分数俩个端点
*/
sort(a,a+n);//可以发现选的数其实跟位置没关系 因为俩个数 总是前后关系
LL ans = 0;
for(LL i = 0;i < n;i++)
{
LL res = 0;
LL left = sum - (LL)a[i] - y;//求出左端点大小
LL right = sum - (LL)a[i] - x;//求出右端点大小
LL l = findl(left);//找一下大于等于左端点的最右端的位置
if(a[l] < left) continue;//如果左端点(也就是a[l] 已经为 数组当中最大值)找不到 跳过
LL r = findr(right);
if(r <= i) break;//如果右端点下标是小于i的 那么不用再找了 因为右端点值只会越来越小
//后面的条件越苛刻 当前条件都满足不了 就不用再继续了
if(l <= i) l = i + 1;//我们只计算在i右边的区间 这样避免了重复计算
res = r - l + 1;
ans += res;//累加一下答案
}
cout << ans << endl;
}
int main()
{
LL T;
cin >> T;
while(T--)
{
solve();
}
return 0;
}
Largest Allowed Area(二分答案 + 二维前缀和)

#include<bits/stdc++.h>
using namespace std;
const int N = 2020;
int g[N][N];
int s[N][N];
int n,m;
// 不使用快读会TLE
#define Temp template<typename T>
Temp inline void read(T &x)
{
x=0;T w=1,ch=getchar();
while(!std::isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar();
x=x*w;
}
bool check(int x)
{
//枚举左上横坐标 对应得到右下横坐标
for(int x1 = 1;x1 + x - 1 <= n;x1 ++)
{
int x2 = x1 + x - 1;
//枚举左上纵坐标 对应得到右下纵坐标
for(int y1 = 1;y1 + x - 1 <= m ;y1 ++)
{
int y2 = y1 + x - 1;
int cnt = s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1 - 1][y1 - 1];
if(cnt <= 1) return true;
}
}
return false;
}
void solve()
{
cin >> n >> m;
for(int i = 1;i <= n;i++)
for(int j = 1;j <= m;j++)
{
read(g[i][j]);
}
for(int i = 1;i <= n;i++)
for(int j = 1;j <= m;j++)
s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + g[i][j];
int l = 1,r = min(n,m);//边长最长为min(n,m);
int ans = 1;
while(l <= r)
{
int mid = l + r >> 1;
if(check(mid))
{
ans = mid;
l = mid + 1;
}
else r = mid - 1;
}
cout << ans << endl;
}
int main()
{
int T;
read(T);
while(T--)
{
memset(g,0,sizeof g);
memset(s,0,sizeof s);
solve();
}
return 0;
}
Max Median(二分答案)


#include<bits/stdc++.h>
using namespace std;
const int N = 200010;
int a[N],tmp[N];
int n,k;
int cnt[N];
bool check(int mid)
{
for(int i = 1;i <= n;i++)
{
if(a[i] >= mid) cnt[i] = 1;
else cnt[i] = -1;
}
for(int i = 1;i <= n;i++) cnt[i] += cnt[i-1];
int minv = 0;
for(int i = k;i <= n;i++)
{
if(cnt[i] - minv > 0) return true;
minv = min(cnt[i - k + 1],minv);
}
return false;
}
int main()
{
cin >> n >> k;
for(int i = 1;i <= n;i++)
cin >> a[i],tmp[i] = a[i];
//将元素组升序排序 二分最大中位数
sort(tmp + 1,tmp + n + 1);
int l = 1,r = n;
while(l < r)
{
int mid = l + r + 1 >> 1;
if(check(tmp[mid])) l = mid;
else r = mid - 1;
}
cout << tmp[l] << endl;
return 0;
}
三.快速读写
模板
#include<bits/stdc++.h>
#define Temp template<typename T>
//避免为不同的数据类型重复编写相同逻辑的函数或类
using namespace std;
//这样,对于任何类型 T,只要支持拷贝构造和赋值操作,该函数都可以使用,无需为每种类型单独编写函数。
Temp inline int read(T & x)
{
x = 0;
T w = 1,ch = getchar();
while(ch <= '0' && ch >= '9')
{
if(ch == '-') w *= -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
x = x * w;
}
四.差分
例题
Karen and Coffee

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 200010;
int d[N];
int cnt[N];
int n,q,k;
int main()
{
cin >> n >> k >> q;
for(int i = 0;i < n;i++)
{
int l,r;
cin >> l >> r;
d[l] ++;
d[r+1] --;
}
for(int i = 1;i <= N;i++) d[i] += d[i-1];
for(int i = 1;i <= N;i++)
{
if(d[i] >= k) cnt[i] = 1;
cnt[i] += cnt[i-1];
}
while(q--)
{
int l,r;
cin >> l >> r;
cout << cnt[r] - cnt[l-1] << endl;
}
}
Landscape Generator
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 200010;
LL d[N];//对于前俩个操作 的差分数组
LL tmpd[N];//对于后俩个操作的差分数组 (二次差分)
int n,k;
int main()
{
cin >> n >> k;
while(k--)
{
char op;
int l,r;
cin >> op >> l >> r;
if(op == 'R') d[l] ++,d[r + 1] --;
else if(op == 'D') d[l] --,d[r + 1] ++;
else if(op == 'H')
{
int mid = l + r >> 1;
int len = r - l + 1;
if(len % 2 == 0)//分奇偶讨论
{
tmpd[l] ++,tmpd[mid + 1] --;
tmpd[mid + 2] --,tmpd[r + 2] ++;
}
else
{
tmpd[l] ++,tmpd[mid + 1] -= 2;
tmpd[r + 2] ++;
}
}
else if(op == 'V')
{
int mid = l + r >> 1;
int len = r - l + 1;
if(len % 2 == 0)
{
tmpd[l] --,tmpd[mid + 1] ++;
tmpd[mid + 2] ++,tmpd[r + 2] --;
}
else
{
tmpd[l] --,tmpd[mid + 1] += 2;
tmpd[r + 2] --;
}
}
}
for(int i = 1;i <= n;i++)
{
tmpd[i] += tmpd[i-1];
d[i] += d[i-1] + tmpd[i];
}
for(int i = 1;i <= n;i++) cout << d[i] << endl;
}
五.快速幂(模板以及求逆元)
模板
LL qmi(LL a,LL k,LL p)
{
LL res = 1;
while(k)
{
if(k & 1) res = res * a % p;
k >>= 1;
a = a * a % mod;
}
retrun res;
}
//求逆元
LL vni(LL x)
{
return qmi(x,p-2);
}
例题
Plant(快速幂+数学推导)

up为an down 为bn
可以发现 an - bn = 2^(n-1)
an + bn = 4^(n-1)
可以求出an 注意题目的第一项跟这里第一项不太一样
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int mod = 1000000007;
LL qmi(LL a,LL k)
{
a %= mod;
LL res = 1;
while(k)
{
if(k & 1)
{
res = (res * a) % mod;
}
k >>= 1;
a = (a * a) % mod;
}
return res;
}
LL up,down;
int main()
{
LL n;
cin >> n;
if(n == 0) puts("1");
else cout << (qmi(2,n * 2 - 1) + qmi(2,n-1)) % mod << endl;
}
Hamsters Training(逆元求组合数)


#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100010, mod = 1e9+7;
LL fa[N],fb[N];
LL qmi(LL a,LL b)
{
LL res = 1;
while(b)
{
if(b & 1) res = res * a % mod;
b >>= 1;
a = a * a % mod;
}
return res;
}
LL C(LL n,LL k)
{
return fa[n] * fb[k] % mod * fb[n - k] % mod;
}
void init()
{
fa[0] = fb[0] = 1;
for(int i = 1;i < N;i++)
{
fa[i] = fa[i-1] * i % mod;
fb[i] = qmi(fa[i],mod - 2) % mod;
}
}
void solve()
{
int n;
cin >> n;
cout << C(2 * n - 1,n) << endl;
}
int main()
{
init();
int T;
cin >> T;
while(T--)
{
solve();
}
}


6888

被折叠的 条评论
为什么被折叠?



