二分
1.技能升级
#include<iostream>
#include<algorithm>
#include<cmath>
#define int long long
using namespace std;
int n,m;
int res=0;
int ans;
int result;
pair<int,int> a[100020];
int check(int mid){
int cnt=0;
for(int i=0;i<n;i++){
if(a[i].first>mid){
cnt+=ceil((double)(a[i].first-mid)/(a[i].second));
}
}
return cnt<=m;
}
int sum(int a,int c,int b){
int m=a-b*(c-1);
return (m+a)*c>>1;
}
signed main(){
cin>>n>>m;
for(int i=0;i<n;i++){
cin>>a[i].first>>a[i].second;
}
int l=0,r=1e6+10;
while(l<r){
int mid=l+r>>1;
if(check(mid)){
r=mid;
}
else{
l=mid+1;
}
}
result=m;
for(int i=0;i<n;i++){
//先求次数在求和
if(a[i].first>l){
ans=ceil((double)(a[i].first-l)/(a[i].second));
result-=ans;
res+=sum(a[i].first,ans,a[i].second);
}
}
cout<<res+result*l;
return 0;
}
对于浮点数,向上取整ceil,向下取整floor,四舍五入round
二分模板一共有两个,分别适用于不同情况。
算法思路:假设目标值在闭区间[l, r]中, 每次将区间长度缩小一半,当l = r时,我们就找到了目标值。
yxc版本1
当我们将区间[l, r]划分成[l, mid]和[mid + 1, r]时,其更新操作是r = mid或者l = mid + 1;,计算mid时不需要加1。
C++ 代码模板:
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
return l;
}
yxc版本2
当我们将区间[l, r]划分成[l, mid - 1]和[mid, r]时,其更新操作是r = mid - 1或者l = mid;,此时为了防止死循环,计算mid时需要加1。
C++ 代码模板:
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
前缀和
#include<iostream>
using namespace std;
int arr[510][510];
long long res=0;
int main(){
int n,m;cin>>n>>m;
int k;cin>>k;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>arr[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
arr[i][j]=arr[i][j]+arr[i-1][j]+arr[i][j-1]-arr[i-1][j-1];
}
}
for(int i=1;i<=m;i++){
for(int j=i;j<=m;j++){
for(int r=1,l=1;r<=n;r++){
while(l<=r&&(arr[r][j]-arr[r][i-1]-arr[l-1][j]+arr[l-1][i-1])>k)l++;
if(l<=r) res+=(r-l+1);
}
}
}
cout<<res;
return 0;
}
这里除了前缀和,还重新优化了一下思路
for(int i=1;i<=m;i++){
for(int j=i;j<=m;j++){
for(int r=1,l=1;r<=n;r++){
while(l<=r&&(arr[r][j]-arr[r][i-1]-arr[l-1][j]+arr[l-1][i-1])>k)l++;
if(l<=r) res+=(r-l+1);
}
}
}
其中i和j是该矩阵的列,内部循环利用双指针,根据矩阵和递增的原理
递增三元组
二分法版本
#include<iostream>
#include<algorithm>
using namespace std;
long long res=0;int n;int arr[4][100010];
int check(int m,int x){
int l=1;int r=n;
while(l<r){
int mid=l+r>>1;
if(arr[x+1][mid]>m){
r=mid;
}
else l=mid+1;
}
return l;
}
int main(){
cin>>n;
for(int i=1;i<=3;i++){
for(int j=1;j<=n;j++){
cin>>arr[i][j];
}
}
for(int i=1;i<=3;i++){
sort(arr[i]+1,arr[i]+n+1);
}
for (int i = 1; i <= n; i ++ ){
int tmp=arr[2][i];
int x=lower_bound(arr[1]+1,arr[1]+n+1,tmp)-arr[1]-1;
int y=upper_bound(arr[3]+1,arr[3]+n+1,tmp)-arr[3];
if(x>=1&&y<=n)
res+=(long long)x*(n-y+1);
}
cout<<res;
return 0;
}
暴力做法本题需要枚举三行,复杂度是n3,肯定会超时,那么我们先理解他的题意:三个符合匹配条件的数a<b<c,那么我们可以先将这三行序列排序最后相乘就能求出结果
差分
1.空调
ac code
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010;
int arr[N];
int mx=0,mi=0;
int main(){
int n;cin>>n;
for (int i = 1; i <= n; i ++ ){
cin>>arr[i];
}
for (int i = 1; i <= n; i ++ ){
int a;cin>>a;
arr[i]=arr[i]-a;
}
//构造差分数组
//这个为什么要从后往前构造呢,因为他是在原数组中进行变换,如果
//从第一个开始构造,那么后面的会相互影响,所以从后面构造不会影响
for (int i = n; i >1; i -- ){
arr[i]-=arr[i-1];
}
//对于差分数组,求得最大次数
for (int i = 1; i <= n; i ++ ){
if(arr[i]>0)mx+=arr[i];
else mi-=arr[i];
}
cout<<max(mi,mx)<<endl;
return 0;
}
这一题主要在于分析思路,要求最优改变几次区间温度,能使每个牛栏获得最佳温度,那么我们可以对差值数组求差分,当差分数组为零时,那么即为最合适温度,要使差分数组为零,要么整个区间加或减,要么对单个区间进行操作
差分矩阵
ac code
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int arr[1010][1010];int b[1010][1010];
void insert(int x1,int y1,int x2,int y2,int x){
b[x1][y1]+=x;
b[x1][y2+1]-=x;
b[x2+1][y1]-=x;
b[x2+1][y2+1]+=x;
}
int main(){
int n,m;cin>>n>>m;
int k;cin>>k;
for (int i = 1; i <= n; i ++ ){
for (int j = 1; j <= m; j ++ ){
cin>>arr[i][j];
}
}
//创建差分矩阵
for (int i = 1; i <= n; i ++ ){
for (int j = 1; j <= m; j ++ ){
b[i][j]=arr[i][j]-arr[i-1][j]-arr[i][j-1]+arr[i-1][j-1];
}
}
//对于每个样例插入差分矩阵
while(k--){
int x1,y1,x2,y2,x;
cin>>x1>>y1>>x2>>y2>>x;
insert(x1,y1,x2,y2,x);
}
//输出改变后的矩阵
for (int i = 1; i <= n; i ++ ){
for (int j = 1; j <= m; j ++ ){
b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
cout<<b[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
差分的重点不在于差分数组的创建,而在于差分数组的维护,这里用一个图形便可以容易理解
代码是这一段
void insert(int x1,int y1,int x2,int y2,int x){
b[x1][y1]+=x;
b[x1][y2+1]-=x;
b[x2+1][y1]-=x;
b[x2+1][y2+1]+=x;
}
原题链接
棋盘
ac代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int arr[2010][2010];
void chance(int x1,int y1,int x2,int y2){
arr[x1][y1]++;
arr[x1][y2+1]--;
arr[x2+1][y1]--;
arr[x2+1][y2+1]++;
}
int main(){
//先创建数组
int n,m;cin>>n>>m;
//构造出差分矩阵
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
arr[i][j]=arr[i][j]-arr[i-1][j]-arr[i][j-1]+arr[i-1][j-1];
}
}
while(m--){
int x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
chance(x1,y1,x2,y2);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
arr[i][j]=arr[i-1][j]+arr[i][j-1]+arr[i][j]-arr[i-1][j-1];
if(arr[i][j]%2==1)cout<<1;
else cout<<0;
}
cout<<endl;
}
return 0;
}
我的疑问是,差分只能对范围内的区间在o1的时间复杂度内同时加上或减去一个数,那么如何把该范围内的1变为0,0变为1呢?
答:根据题意,我们可以分析得,改变次数为奇数次的时候,棋盘都为黑子,改变次数为偶数次的时候,棋盘都为白子,得到这个结论,我们就很容易ac了
双指针
牛的学术圈
本题主要是二分
ac代码
#include<iostream>
using namespace std;
int n,backup;
int arr[100010];
int check(int mid){
int backupp=backup;
int cnt=0;
for(int i=0;i<n;i++){
if(arr[i]>=mid){
cnt++;
}
else if(backupp&&arr[i]+1>=mid){
cnt++;
backupp--;
}
}return cnt>=mid;
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>backup;
for(int i=0;i<n;i++)cin>>arr[i];
int l=0,r=1e6;
while(l<r){
int mid=l+r+1>>1;
if(check(mid)){
l=mid;
}
else {
r=mid-1;
}
}
cout<<l;
return 0;
}
日志统计
ac代码
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int ans[100010];bool mm[100010];
pair<int,int> arr[100010];
int main(){
int a,b,c;cin>>a>>b>>c;//a样例个数,b时间,c点赞数
for(int i=0;i<a;i++){
cin>>arr[i].first>>arr[i].second;
}
sort(arr,arr+a);//对first进行排序
for(int i=0,j=0;i<a;i++){
int tmp=arr[i].first;
ans[arr[i].second]++;
while(tmp-arr[j].first>=b){
ans[arr[j].second]--;
j++;
}
if(ans[arr[i].second]>=c){
mm[arr[i].second]=true;
}
//如果超出这段区间
//如何统计热帖,他不是从一开始
//那就遍历一个统计一个
}
for (int i = 0; i <= 100000; i ++ ) if (mm[i]) cout << i << endl;
return 0;
}
做题时的疑问点:
1.时间不连续,应该如何存储呢,数据较小,可以直接存储
2.i++,走在前面,一旦i-j大于时间差,那么j就往前走,并实时更新