补题日记(8)

区间合并问题

区间和并就是给定n个区间将他们合并 , 我们可以使用PII类型的数组来存储每个数组的左端点和右端点,之后再将他们排序之后合并,下面是模板

区间合并的模板

#define x first
#define y second 
typedef pair<int,int>PII;

vector<PII>v;


void merge(vector<PII>&v){
    vector<PII>ans;    
    sort(v.begin(),v.end() ); 
    int st = -2e9 , ed = -2e9;
    for(auto V:v){
        if(ed <V.x){  //这是不能被合并的情况
            if(st != -2e9)ans.push_back({st , ed});
            st =V.x , ed = V.y;
        }
        else ed = max(ed ,V.y);  //可以被合并的情况
    
    }
    if(st != -2e9) ans.push_back({st.ed});
    v = ans;

}

题目 : 管道

5407. 管道 - AcWing题库

这题也是蓝桥杯真题 , 这题的思路也很简单,需要用到二分和区间合并的方法。

这里就直接给出代码了

#include <bits/stdc++.h>
using namespace std;
#define int long long
int n , m;
typedef pair<int,int> PII;
#define x first 
#define y second 
vector<PII>v;

bool check(int x){
    vector<PII>ans;
    for(int i =0;i<n;++i){
        int idx = v[i].x , S = v[i].y;
        if(x >= S){
            int t = x - S;
            int l =max(1ll ,idx -t );
            int r =min(m ,idx + t );
            ans.push_back({l , r});
        } 
    }
    sort(ans.begin() , ans.end());
    int st = -1 , ed = -1;
    for(auto V:ans){
        if(ed < V.x-1){
            if(st != -1)ans.push_back({st,ed});  //这一步在这道题其实可以不需要写
            st = V.x , ed = V.y;
        }
        else  ed = max(ed , V.y);
    }
    return st ==1 && ed == m;

}


void solve(){
    cin>>n>>m;
    for(int i =1;i<=n;++i){
        int a , b;cin>>a>>b;
        v.push_back({a,b}); //表示在位于a的阀门第b个时刻打开
    }
    int l =0 , r = 2*m;  //开始二分
    while(l < r){
        int mid = (l + r)>>1;
        if(check(mid))r = mid;
        else l = mid +1;
    }
    cout<<r;


}




signed main(){
    ios::sync_with_stdio(0),cout.tie(0),cin.tie(0);
    solve();

    return 0;
}

挤牛奶

1343. 挤牛奶 - AcWing题库

这题也是一个区间合并 ,我们需要先将区间合并,然后查找

#include <bits/stdc++.h>
using namespace std;
#define int long long

typedef pair<int,int> PII;
#define x first 
#define y second 
vector<PII>v;
int n;
int have = 0 , no = 0;
void merge(vector<PII>&v ){
    vector<PII>res;
    sort(v.begin() ,v.end());

    int st = -1 , ed = -1;
    for(auto V:v){
        if(ed <V.x ){
            if(st != -1 ){
                res.push_back({st,ed}) ;
                have = max(have ,ed - st);
                no = max(no , V.x - ed);
            }
            st = V.x , ed = V.y ;
            
        }
        else ed = max(ed , V.y) , have = max(have ,ed - st);
    }
    if(st != -1)res.push_back({st,ed}) , have = max(have , ed - st);
    v = res;
    return ;
}



void solve(){
    cin>>n;
    for(int i =1;i<=n;++i){
        int a , b;cin>>a>>b;
        v.push_back({a,b});
    }
    //区间和并
    merge(v);  // 300 1200 1500 2100
    cout<<have<<' '<<no;
    
}


signed main(){
    ios::sync_with_stdio(0),cout.tie(0),cin.tie(0);
    solve();

    return 0;
}

日期问题

模板

const int month[]{   //12个月份
    0,31,28,31,30,31,30,31,31,30,31,30,31
};

int is_leap(int year){  //判断是否是闰年
    if(year %4 ==0 && year %100 || year %400 == 0)return 1;
    return 0;
}

int get_days(int y ,int m ){  //得到每个月有多少天
    if(m==2)return 28 +is_leap(y);
    else return month[m];
}


日期差值

3498. 日期差值 - AcWing题库


#include <bits/stdc++.h>
using namespace std;
#define endl '\n'

const int month[] {
    0,31,28,31,30,31,30,31,31,30,31,30,31
};

int is_leap(int year){
    if( (year%4==0 && year%100 )||year % 400 == 0 )return 1;
    return 0;
}

int get_days(int y ,int m){
    if(m == 2)return 28 + is_leap(y);
    else return month[m];
}


int calc(int x){
    int y = x / 10000;
    int m = x%10000/100;
    int d = x%10000%100; 
    int res = 0;
    for(int i =1;i<y;++i)res += 365 + is_leap(i);
    for(int i =1;i<m;++i){
        res +=get_days(y , i);
    }
    return res + d;

}



int main(){
    int a ,b;
    while(cin>>a>>b){
        cout<<abs(calc(a) - calc(b))+1;
        cout<<endl;
    }
    


}

贡献法

贡献法就是计算对答案的贡献

字串分值

这题的样例abaabc ,我们对最中间的a来看 ,左边是 ab ,右边是abc,那么我们分开来看,先看左边是ab那么,对答案有一个贡献,对于右边是abc对答案没有贡献,因为右边的第一个就是a,那么我们对每个数字都这样求贡献即可

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5+9;
#define int long long
char s[N];
int ans;
signed main(){
    cin>>s+1;
    int n =strlen(s+1);
    int   j = 0, k = 0;
   
  
    for(int i =1;i<=n;++i){
        char ch = s[i];

        for( j = i-1;j>=1;--j){
            if(s[j] == ch)break;  //得到一个j的值 左边
        }

        for( k = i+1;k<=n;++k){  //右边
            if(s[k] == ch)break;
        }
        ans +=(i - j) * (k - i);
    }

    cout<<ans;


    return 0;
}

二维差分和二维前缀和

给出模板

二维前缀和的模板

for(int i =1;i<=n;++i){
    for(int j =1;j<=m;++j){
        pre[i][j] = pre[i-1][j] + pre[i][j-1] - pre[i-1][j-1]+a[i][j];
        
    }
}

while(q--){
    int x1 , y1 , x2 , y2;
    cin>>x1>>y1>>x2>>y2;
    int ans = pre[x2][y2] - pre[x1-1][y2] - pre[x2][y1-1] + pre[x1-1][y1-1];
    cout<<ans;
}

二维差分的模板

void insert(int x1 , int y1 , int x2 ,int y2,int c){
    dif[x1][y1] +=c;
    dif[x2+1][y1] -=c;
    dif[x1+1][y2] -=c;
    dif[x2+1][y2+1] +=c;
}



for(int i =1;i<=n;++i){
    for(int j =1;j<=m;++j){
        insert(i , j ,i , j , a[i][j]);
    }
}

while(q--){
    int x1 , y1 , x2 , y2,c;
    cin>>x1>>y1>>x2>>y2>>c;
    
}


for( int i =1;i<=n;++i){
    for(int j =1;j<=m;++j){
        dif[i][j] += dif[i-1][j] + dif[i][j-1] - dif[i-1][j-1];
    }
}

归并排序和逆序对

给出归并排序模板

​
void merge_sort(int l , int r){
    if(l>=r)return;
    int mid = (l+r)>>1;
    int k =0 , i = l , j = mid+1;
    while(i <=mid && j <=r){
        if(a[i] <= a[j])t[k++] = a[i++];
        else t[k++] = a[j++];
    }
    while(i <= mid )t[k++] = a[i++];
    while(j <= r)t[k++] = a[j++];
    for(i = l , j = 0;i<=r;i++ ,j++)a[i] = t[j];
    
}




int a[N] , t[N];
cin>>n;
for(int i =1;i<=n;++i)cin>>a[i];
cout<<merge_sort(1 , n);

​

  逆序对

  P1908 逆序对 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e5+9;
int n;
int a[N] , t[N];

int merge_sort(int l , int r){
    if(l >=r)return 0;
    int mid = (l+r)>>1;

    int cnt =merge_sort(l , mid)+merge_sort(mid+1 , r);
    
    int k = 0 , i =l , j = mid+1;
    while(i <= mid && j <= r){
        if(a[i] <= a[j])t[k++] = a[i++];
        else t[k++] = a[j++] , cnt += mid -i +1;
    }
    while(i <=mid)t[k++] = a[i++];
    while(j <= r)t[k++] = a[j++];
    for( i =l , j = 0;i<=r;++i , ++j)a[i] = t[j];
    return cnt;
}


signed main(){
    cin>>n;
    for(int i =1;i<=n;++i)cin>>a[i];

     
    cout<<merge_sort(1 , n);

    return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值