一些小问题
写比赛是输入最好用scanf();
当题目有多组输入的时候 while(scanf("%d %d",&a,&b)!=EOF)
输入输出 全部改成 scanf() 和 printf();
当输出空格时 用这个printf("\n");
优先队列
#include<bits/stdc++.h>
#include<queue>
typedef long long ll;
using namespace std;
const int maxt=1e5+10;
priority_queue<int,vector<int>,less<int> >q1;//从大到小
//这个也可以写成 priority_queue<int>q1 这种默认从大到小排列
priority_queue<int,vector<int>,greater<int> >q2;//从小到大
int main()
{
int h[10]={4,3,2,1,5,6,7,9,8};
for(int i=0;i <9;i ++){
q1.push(h[i]);
q2.push(h[i]);
}
while(q1.size()){
cout<<q1.top()<<" "; //对于普通队列 返回第一个数 为q1.front()
q1.pop();//优先队列 返回第一个数为 q1.top();
}
cout<<endl;
while(q2.size()){
cout<<q2.top()<<" ";
q2.pop();
}
return 0;
}
//输出
// 9 8 7 6 5 4 3 2 1
// 1 2 3 4 5 6 7 8 9
lower_bound()和upper_bound()
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxt=10000;
int main()
{
int h[10]={1,2,3,4,5,6,7,8,9};
int x=upper_bound(h,h+9,7)-h;//在数组中找到第一个大于x的下标
int y=lower_bound(h,h+9,7)-h;//在数组中找到第一个大于或等于x的下标
cout<<x<<" "<<h[x]<<endl;
cout<<y<<" "<<h[y]<<endl<<endl;
int k[10]={9,8,7,6,5,4,3,2,1};
int a=upper_bound(k,k+9,7,greater<int>())-k;//求在k数组中第一个小于7的数的下标
int b=lower_bound(k,k+9,7,greater<int>())-k;//求在k数组中第一个小于或等于7的数的下标
cout<<a<<" "<<k[a]<<endl;
cout<<b<<" "<<k[b]<<endl;
return 0;
}
/*
7 8
6 7
3 6
2 7
*/
数据结构
Flood fill
哈希 找到所有的连通块
形状相同的连通块映射到同一个数
尽量避免冲突
对于不同的连通块可以通过计算他们的欧几里得距离来计算他们的映射的数
求逆序对 (用归并排序比用树状数组要快)
例题洛谷 P1908 原题链接 https://www.luogu.com.cn/problem/P1908
归并解法
时间复杂度 (n log n)
归并就是一直2分 然后对子序列进行合并
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<cstring>
typedef long long ll;
using namespace std;
const int maxt = 501000;
//归并排序 用于寻找逆序对的个数
//会爆int 所以用ll
ll h[maxt], k[maxt], cnt;
void merge(int l, int r) {
if (l >= r) return;
int mid = (l + r) >> 1;
merge(l, mid);
merge(mid + 1, r);
int i = l, j = mid+1, t = 0;
while (i <= mid && j <= r) {
if (h[i] <= h[j]) k[t++] = h[i++];
else {
k[t++] = h[j++];
cnt += mid - i + 1;//关键所在 直接找出了逆序对的对数
}
}
while (i <= mid) k[t++] = h[i++];
while (j <= r)k[t++] = h[j++];
for (i = l, j = 0; i <= r; i++,j++)
h[i] = k[j];
}
int main()
{
ll n;
scanf("%lld", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld", &h[i]);
}
merge(1, n );
printf("%lld\n", cnt);
return 0;
}
树状数组解法
重要概念 离散化
结构体存储 把每个数的位置存入树状数组中
因为排过序了 后面前面进入的数一定比后面进来的数要大
然后查询这个数前面位置的数(是前面这个位置的数 所以要当前位置减一)
就是逆序对的个数
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<cstring>
typedef long long ll;
using namespace std;
const int maxt = 501000;
int k[maxt],n,cnt[maxt];
struct node {
int val;
int a;
}h[maxt];
bool cmp(node x, node y) {
if (x.val == y.val)//判断重复的 先出现的标记更小
return x.a < y.a;
else
return x.val < y.val;
}
int lowbit(int x) {
return x & -x;
}
void add(int x) {
for (int i = x; i < maxt; i += lowbit(i)) k[i] ++;
}
int query(int x) {
int res = 0;
for (int i = x; i > 0; i-=lowbit(i)) res += k[i];
return res;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++) {
scanf("%lld", &h[i].val);
h[i].a = i;
}
sort(h + 1, h + n + 1, cmp);
ll res = 0;
for (int i = 1; i <= n; i++)
cnt[h[i].a]=i;
for (int i = 1; i <= n; i++) {
add(cnt[i]);
res +=i-query(cnt[i]);
}
printf("%lld\n", res);
return 0;
}
**线段树模板**
单点修改
区间修改
区间求和
```cpp
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxt = 100100;
ll h[maxt], tree[maxt * 4], lazy[maxt * 4];
void pushdown(ll nod, ll l, ll r) {//向下更新 因为这个lazy数组很懒 所以只有用到它是才会往下更新
if (lazy[nod]) {
lazy[nod * 2] += lazy[nod];
lazy[nod * 2 + 1] += lazy[nod];
int mid = (l + r) / 2;
tree[nod * 2] += lazy[nod] * (mid - l + 1);
tree[nod * 2 + 1] += lazy[nod] * (r - mid);
lazy[nod] = 0;
}
}
void build(ll l, ll r, ll nod) {//建树
if (l == r) {
tree[nod] = h[r];
return;
}
int mid = (l + r) / 2;
build(l, mid, nod * 2);
build(mid + 1, r, nod * 2 + 1);
tree[nod] = tree[nod * 2] + tree[nod * 2 + 1];
}
void update1(ll s, ll t, ll l, ll r, ll nod) {//单点修改 h[s]+=t;
if (l == r) {
tree[nod] += t;
}
else {
int mid = (l + r) / 2;
update(s, t, l, mid, nod * 2);
update(s, t, mid + 1, r, nod * 2 + 1);
tree[nod] = tree[nod * 2] + tree[nod * 2 + 1];
}
}
void update(ll L,ll R,ll val,ll l,ll r,ll nod) {//区间修改操作 在区间L到R之间都加上一个val
if (R < l || r < L) return;
if (L <= l && r <= R) {
tree[nod] += (r - l + 1) * val;
lazy[nod] += val;
}
else {
pushdown(nod, l, r);
int mid = (l + r) / 2;
update(L, R, val, l, mid, nod * 2);
update(L, R, val, mid + 1, r, nod * 2 + 1);
tree[nod] = tree[nod * 2] + tree[nod * 2 + 1];
}
}
ll query(ll L, ll R, ll l, ll r, ll nod) {//求和操作
ll res = 0;
if (L <= l && R >= r) return tree[nod];
pushdown(nod, l, r);
int mid = (l + r) / 2;
if (L <= mid) res += query(L, R, l, mid, nod * 2);
if(R>mid) res+=query(L, R, mid + 1, r, nod * 2 + 1);
return res;
}
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> h[i];
build(1, n, 1);
int a, b, c, d;
while (m--) {
cin >> a;
ll res = 0;
if (a == 1) {
cin >> b >> c >> d;
update(b, c, d, 1, n, 1);
}
else {
cin >> b >> c;
res = query(b, c, 1, n, 1);
printf("%lld\n", res);
}
}
return 0;
}