区间合并问题
区间和并就是给定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;
}
题目 : 管道
这题也是蓝桥杯真题 , 这题的思路也很简单,需要用到二分和区间合并的方法。
这里就直接给出代码了
#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;
}
挤牛奶
这题也是一个区间合并 ,我们需要先将区间合并,然后查找
#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];
}
日期差值
#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;
}